prometheus-client-mmap 0.20.3-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 +7 -0
 - data/README.md +253 -0
 - data/ext/fast_mmaped_file/extconf.rb +30 -0
 - data/ext/fast_mmaped_file/fast_mmaped_file.c +122 -0
 - data/ext/fast_mmaped_file/file_format.c +5 -0
 - data/ext/fast_mmaped_file/file_format.h +11 -0
 - data/ext/fast_mmaped_file/file_parsing.c +195 -0
 - data/ext/fast_mmaped_file/file_parsing.h +27 -0
 - data/ext/fast_mmaped_file/file_reading.c +102 -0
 - data/ext/fast_mmaped_file/file_reading.h +30 -0
 - data/ext/fast_mmaped_file/globals.h +14 -0
 - data/ext/fast_mmaped_file/mmap.c +427 -0
 - data/ext/fast_mmaped_file/mmap.h +61 -0
 - data/ext/fast_mmaped_file/rendering.c +199 -0
 - data/ext/fast_mmaped_file/rendering.h +8 -0
 - data/ext/fast_mmaped_file/utils.c +56 -0
 - data/ext/fast_mmaped_file/utils.h +22 -0
 - data/ext/fast_mmaped_file/value_access.c +242 -0
 - data/ext/fast_mmaped_file/value_access.h +15 -0
 - data/ext/fast_mmaped_file_rs/.cargo/config.toml +23 -0
 - data/ext/fast_mmaped_file_rs/Cargo.lock +790 -0
 - data/ext/fast_mmaped_file_rs/Cargo.toml +30 -0
 - data/ext/fast_mmaped_file_rs/README.md +52 -0
 - data/ext/fast_mmaped_file_rs/extconf.rb +30 -0
 - data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
 - data/ext/fast_mmaped_file_rs/src/file_entry.rs +579 -0
 - data/ext/fast_mmaped_file_rs/src/file_info.rs +190 -0
 - data/ext/fast_mmaped_file_rs/src/lib.rs +79 -0
 - data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
 - data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
 - data/ext/fast_mmaped_file_rs/src/mmap.rs +151 -0
 - data/ext/fast_mmaped_file_rs/src/parser.rs +346 -0
 - data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
 - data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
 - data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
 - data/lib/2.7/fast_mmaped_file.so +0 -0
 - data/lib/2.7/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.0/fast_mmaped_file.so +0 -0
 - data/lib/3.0/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.1/fast_mmaped_file.so +0 -0
 - data/lib/3.1/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.2/fast_mmaped_file.so +0 -0
 - data/lib/3.2/fast_mmaped_file_rs.so +0 -0
 - data/lib/prometheus/client/configuration.rb +23 -0
 - data/lib/prometheus/client/counter.rb +27 -0
 - data/lib/prometheus/client/formats/text.rb +118 -0
 - data/lib/prometheus/client/gauge.rb +40 -0
 - data/lib/prometheus/client/helper/entry_parser.rb +132 -0
 - data/lib/prometheus/client/helper/file_locker.rb +50 -0
 - data/lib/prometheus/client/helper/json_parser.rb +23 -0
 - data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
 - data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
 - data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
 - data/lib/prometheus/client/helper/plain_file.rb +29 -0
 - data/lib/prometheus/client/histogram.rb +80 -0
 - data/lib/prometheus/client/label_set_validator.rb +86 -0
 - data/lib/prometheus/client/metric.rb +80 -0
 - data/lib/prometheus/client/mmaped_dict.rb +79 -0
 - data/lib/prometheus/client/mmaped_value.rb +154 -0
 - data/lib/prometheus/client/page_size.rb +17 -0
 - data/lib/prometheus/client/push.rb +203 -0
 - data/lib/prometheus/client/rack/collector.rb +88 -0
 - data/lib/prometheus/client/rack/exporter.rb +96 -0
 - data/lib/prometheus/client/registry.rb +65 -0
 - data/lib/prometheus/client/simple_value.rb +31 -0
 - data/lib/prometheus/client/summary.rb +69 -0
 - data/lib/prometheus/client/support/unicorn.rb +35 -0
 - data/lib/prometheus/client/uses_value_type.rb +20 -0
 - data/lib/prometheus/client/version.rb +5 -0
 - data/lib/prometheus/client.rb +58 -0
 - data/lib/prometheus.rb +3 -0
 - data/vendor/c/hashmap/.gitignore +52 -0
 - data/vendor/c/hashmap/LICENSE +21 -0
 - data/vendor/c/hashmap/README.md +90 -0
 - data/vendor/c/hashmap/_config.yml +1 -0
 - data/vendor/c/hashmap/src/hashmap.c +692 -0
 - data/vendor/c/hashmap/src/hashmap.h +267 -0
 - data/vendor/c/hashmap/test/Makefile +22 -0
 - data/vendor/c/hashmap/test/hashmap_test.c +608 -0
 - data/vendor/c/jsmn/.travis.yml +4 -0
 - data/vendor/c/jsmn/LICENSE +20 -0
 - data/vendor/c/jsmn/Makefile +41 -0
 - data/vendor/c/jsmn/README.md +168 -0
 - data/vendor/c/jsmn/example/jsondump.c +126 -0
 - data/vendor/c/jsmn/example/simple.c +76 -0
 - data/vendor/c/jsmn/jsmn.c +314 -0
 - data/vendor/c/jsmn/jsmn.h +76 -0
 - data/vendor/c/jsmn/library.json +16 -0
 - data/vendor/c/jsmn/test/test.h +27 -0
 - data/vendor/c/jsmn/test/tests.c +407 -0
 - data/vendor/c/jsmn/test/testutil.h +94 -0
 - metadata +243 -0
 
| 
         @@ -0,0 +1,473 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            use std::mem::size_of;
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            use crate::error::MmapError;
         
     | 
| 
      
 4 
     | 
    
         
            +
            use crate::util;
         
     | 
| 
      
 5 
     | 
    
         
            +
            use crate::util::CheckedOps;
         
     | 
| 
      
 6 
     | 
    
         
            +
            use crate::Result;
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            /// The logic to save a `MetricsEntry`, or parse one from a byte slice.
         
     | 
| 
      
 9 
     | 
    
         
            +
            #[derive(PartialEq, Clone, Debug)]
         
     | 
| 
      
 10 
     | 
    
         
            +
            pub struct RawEntry<'a> {
         
     | 
| 
      
 11 
     | 
    
         
            +
                bytes: &'a [u8],
         
     | 
| 
      
 12 
     | 
    
         
            +
                encoded_len: usize,
         
     | 
| 
      
 13 
     | 
    
         
            +
            }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            impl<'a> RawEntry<'a> {
         
     | 
| 
      
 16 
     | 
    
         
            +
                /// Save an entry to the mmap, returning the value offset in the newly created entry.
         
     | 
| 
      
 17 
     | 
    
         
            +
                pub fn save(bytes: &'a mut [u8], key: &[u8], value: f64) -> Result<usize> {
         
     | 
| 
      
 18 
     | 
    
         
            +
                    let total_len = Self::calc_total_len(key.len())?;
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    if total_len > bytes.len() {
         
     | 
| 
      
 21 
     | 
    
         
            +
                        return Err(MmapError::Other(format!(
         
     | 
| 
      
 22 
     | 
    
         
            +
                            "entry length {total_len} larger than slice length {}",
         
     | 
| 
      
 23 
     | 
    
         
            +
                            bytes.len()
         
     | 
| 
      
 24 
     | 
    
         
            +
                        )));
         
     | 
| 
      
 25 
     | 
    
         
            +
                    }
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    // CAST: `calc_len` runs `check_encoded_len`, we know the key len
         
     | 
| 
      
 28 
     | 
    
         
            +
                    // is less than i32::MAX. No risk of overflows or failed casts.
         
     | 
| 
      
 29 
     | 
    
         
            +
                    let key_len: u32 = key.len() as u32;
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    // Write the key length to the mmap.
         
     | 
| 
      
 32 
     | 
    
         
            +
                    bytes[..size_of::<u32>()].copy_from_slice(&key_len.to_ne_bytes());
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    // Advance slice past the size.
         
     | 
| 
      
 35 
     | 
    
         
            +
                    let bytes = &mut bytes[size_of::<u32>()..];
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    bytes[..key.len()].copy_from_slice(key);
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    // Advance to end of key.
         
     | 
| 
      
 40 
     | 
    
         
            +
                    let bytes = &mut bytes[key.len()..];
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    let pad_len = Self::padding_len(key.len());
         
     | 
| 
      
 43 
     | 
    
         
            +
                    bytes[..pad_len].fill(b' ');
         
     | 
| 
      
 44 
     | 
    
         
            +
                    let bytes = &mut bytes[pad_len..];
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    bytes[..size_of::<f64>()].copy_from_slice(&value.to_ne_bytes());
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    Self::calc_value_offset(key.len())
         
     | 
| 
      
 49 
     | 
    
         
            +
                }
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                /// Parse a byte slice starting into an `MmapEntry`.
         
     | 
| 
      
 52 
     | 
    
         
            +
                pub fn from_slice(bytes: &'a [u8]) -> Result<Self> {
         
     | 
| 
      
 53 
     | 
    
         
            +
                    // CAST: no-op on 32-bit, widening on 64-bit.
         
     | 
| 
      
 54 
     | 
    
         
            +
                    let encoded_len = util::read_u32(bytes, 0)? as usize;
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    let total_len = Self::calc_total_len(encoded_len)?;
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    // Confirm the value is in bounds of the slice provided.
         
     | 
| 
      
 59 
     | 
    
         
            +
                    if total_len > bytes.len() {
         
     | 
| 
      
 60 
     | 
    
         
            +
                        return Err(MmapError::out_of_bounds(total_len, bytes.len()));
         
     | 
| 
      
 61 
     | 
    
         
            +
                    }
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    // Advance slice past length int and cut at end of entry.
         
     | 
| 
      
 64 
     | 
    
         
            +
                    let bytes = &bytes[size_of::<u32>()..total_len];
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    Ok(Self { bytes, encoded_len })
         
     | 
| 
      
 67 
     | 
    
         
            +
                }
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                /// Read the `f64` value of an entry from memory.
         
     | 
| 
      
 70 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 71 
     | 
    
         
            +
                pub fn value(&self) -> f64 {
         
     | 
| 
      
 72 
     | 
    
         
            +
                    // We've stripped off the leading u32, don't include that here.
         
     | 
| 
      
 73 
     | 
    
         
            +
                    let offset = self.encoded_len + Self::padding_len(self.encoded_len);
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    // UNWRAP: We confirm in the constructor that the value offset
         
     | 
| 
      
 76 
     | 
    
         
            +
                    // is in-range for the slice.
         
     | 
| 
      
 77 
     | 
    
         
            +
                    util::read_f64(self.bytes, offset).unwrap()
         
     | 
| 
      
 78 
     | 
    
         
            +
                }
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                /// The length of the entry key without padding.
         
     | 
| 
      
 81 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 82 
     | 
    
         
            +
                pub fn encoded_len(&self) -> usize {
         
     | 
| 
      
 83 
     | 
    
         
            +
                    self.encoded_len
         
     | 
| 
      
 84 
     | 
    
         
            +
                }
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                /// Returns a slice with the JSON string in the entry, excluding padding.
         
     | 
| 
      
 87 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 88 
     | 
    
         
            +
                pub fn json(&self) -> &[u8] {
         
     | 
| 
      
 89 
     | 
    
         
            +
                    &self.bytes[..self.encoded_len]
         
     | 
| 
      
 90 
     | 
    
         
            +
                }
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                /// Calculate the total length of an `MmapEntry`, including the string length,
         
     | 
| 
      
 93 
     | 
    
         
            +
                /// string, padding, and value.
         
     | 
| 
      
 94 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 95 
     | 
    
         
            +
                pub fn total_len(&self) -> usize {
         
     | 
| 
      
 96 
     | 
    
         
            +
                    // UNWRAP:: We confirmed in the constructor that this doesn't overflow.
         
     | 
| 
      
 97 
     | 
    
         
            +
                    Self::calc_total_len(self.encoded_len).unwrap()
         
     | 
| 
      
 98 
     | 
    
         
            +
                }
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                /// Calculate the total length of an `MmapEntry`, including the string length,
         
     | 
| 
      
 101 
     | 
    
         
            +
                /// string, padding, and value. Validates encoding_len is within expected bounds.
         
     | 
| 
      
 102 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 103 
     | 
    
         
            +
                pub fn calc_total_len(encoded_len: usize) -> Result<usize> {
         
     | 
| 
      
 104 
     | 
    
         
            +
                    Self::calc_value_offset(encoded_len)?.add_chk(size_of::<f64>())
         
     | 
| 
      
 105 
     | 
    
         
            +
                }
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                /// Calculate the value offset of an `MmapEntry`, including the string length,
         
     | 
| 
      
 108 
     | 
    
         
            +
                /// string, padding. Validates encoding_len is within expected bounds.
         
     | 
| 
      
 109 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 110 
     | 
    
         
            +
                pub fn calc_value_offset(encoded_len: usize) -> Result<usize> {
         
     | 
| 
      
 111 
     | 
    
         
            +
                    Self::check_encoded_len(encoded_len)?;
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    Ok(size_of::<u32>() + encoded_len + Self::padding_len(encoded_len))
         
     | 
| 
      
 114 
     | 
    
         
            +
                }
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                /// Calculate the number of padding bytes to add to the value key to reach
         
     | 
| 
      
 117 
     | 
    
         
            +
                /// 8-byte alignment. Does not validate key length.
         
     | 
| 
      
 118 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 119 
     | 
    
         
            +
                pub fn padding_len(encoded_len: usize) -> usize {
         
     | 
| 
      
 120 
     | 
    
         
            +
                    8 - (size_of::<u32>() + encoded_len) % 8
         
     | 
| 
      
 121 
     | 
    
         
            +
                }
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                #[inline]
         
     | 
| 
      
 124 
     | 
    
         
            +
                fn check_encoded_len(encoded_len: usize) -> Result<()> {
         
     | 
| 
      
 125 
     | 
    
         
            +
                    if encoded_len as u64 > i32::MAX as u64 {
         
     | 
| 
      
 126 
     | 
    
         
            +
                        return Err(MmapError::KeyLength);
         
     | 
| 
      
 127 
     | 
    
         
            +
                    }
         
     | 
| 
      
 128 
     | 
    
         
            +
                    Ok(())
         
     | 
| 
      
 129 
     | 
    
         
            +
                }
         
     | 
| 
      
 130 
     | 
    
         
            +
            }
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            #[cfg(test)]
         
     | 
| 
      
 133 
     | 
    
         
            +
            mod test {
         
     | 
| 
      
 134 
     | 
    
         
            +
                use bstr::ByteSlice;
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                use super::*;
         
     | 
| 
      
 137 
     | 
    
         
            +
                use crate::testhelper::TestEntry;
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 140 
     | 
    
         
            +
                fn test_from_slice() {
         
     | 
| 
      
 141 
     | 
    
         
            +
                    #[derive(PartialEq, Default, Debug)]
         
     | 
| 
      
 142 
     | 
    
         
            +
                    struct TestCase {
         
     | 
| 
      
 143 
     | 
    
         
            +
                        name: &'static str,
         
     | 
| 
      
 144 
     | 
    
         
            +
                        input: TestEntry,
         
     | 
| 
      
 145 
     | 
    
         
            +
                        expected_enc_len: Option<usize>,
         
     | 
| 
      
 146 
     | 
    
         
            +
                        expected_err: Option<MmapError>,
         
     | 
| 
      
 147 
     | 
    
         
            +
                    }
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                    let tc = vec![
         
     | 
| 
      
 150 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 151 
     | 
    
         
            +
                            name: "ok",
         
     | 
| 
      
 152 
     | 
    
         
            +
                            input: TestEntry {
         
     | 
| 
      
 153 
     | 
    
         
            +
                                header: 61,
         
     | 
| 
      
 154 
     | 
    
         
            +
                                json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 155 
     | 
    
         
            +
                                padding_len: 7,
         
     | 
| 
      
 156 
     | 
    
         
            +
                                value: 1.0,
         
     | 
| 
      
 157 
     | 
    
         
            +
                            },
         
     | 
| 
      
 158 
     | 
    
         
            +
                            expected_enc_len: Some(61),
         
     | 
| 
      
 159 
     | 
    
         
            +
                            ..Default::default()
         
     | 
| 
      
 160 
     | 
    
         
            +
                        },
         
     | 
| 
      
 161 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 162 
     | 
    
         
            +
                            name: "zero length key",
         
     | 
| 
      
 163 
     | 
    
         
            +
                            input: TestEntry {
         
     | 
| 
      
 164 
     | 
    
         
            +
                                header: 0,
         
     | 
| 
      
 165 
     | 
    
         
            +
                                json: "",
         
     | 
| 
      
 166 
     | 
    
         
            +
                                padding_len: 4,
         
     | 
| 
      
 167 
     | 
    
         
            +
                                value: 1.0,
         
     | 
| 
      
 168 
     | 
    
         
            +
                            },
         
     | 
| 
      
 169 
     | 
    
         
            +
                            expected_enc_len: Some(0),
         
     | 
| 
      
 170 
     | 
    
         
            +
                            ..Default::default()
         
     | 
| 
      
 171 
     | 
    
         
            +
                        },
         
     | 
| 
      
 172 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 173 
     | 
    
         
            +
                            name: "header value too large",
         
     | 
| 
      
 174 
     | 
    
         
            +
                            input: TestEntry {
         
     | 
| 
      
 175 
     | 
    
         
            +
                                header: i32::MAX as u32 + 1,
         
     | 
| 
      
 176 
     | 
    
         
            +
                                json: "foo",
         
     | 
| 
      
 177 
     | 
    
         
            +
                                padding_len: 1,
         
     | 
| 
      
 178 
     | 
    
         
            +
                                value: 0.0,
         
     | 
| 
      
 179 
     | 
    
         
            +
                            },
         
     | 
| 
      
 180 
     | 
    
         
            +
                            expected_err: Some(MmapError::KeyLength),
         
     | 
| 
      
 181 
     | 
    
         
            +
                            ..Default::default()
         
     | 
| 
      
 182 
     | 
    
         
            +
                        },
         
     | 
| 
      
 183 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 184 
     | 
    
         
            +
                            name: "header value much longer than json len",
         
     | 
| 
      
 185 
     | 
    
         
            +
                            input: TestEntry {
         
     | 
| 
      
 186 
     | 
    
         
            +
                                header: 256,
         
     | 
| 
      
 187 
     | 
    
         
            +
                                json: "foobar",
         
     | 
| 
      
 188 
     | 
    
         
            +
                                padding_len: 6,
         
     | 
| 
      
 189 
     | 
    
         
            +
                                value: 1.0,
         
     | 
| 
      
 190 
     | 
    
         
            +
                            },
         
     | 
| 
      
 191 
     | 
    
         
            +
                            expected_err: Some(MmapError::out_of_bounds(272, 24)),
         
     | 
| 
      
 192 
     | 
    
         
            +
                            ..Default::default()
         
     | 
| 
      
 193 
     | 
    
         
            +
                        },
         
     | 
| 
      
 194 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 195 
     | 
    
         
            +
                            // Situations where encoded_len is wrong but padding makes the
         
     | 
| 
      
 196 
     | 
    
         
            +
                            // value offset the same are not caught.
         
     | 
| 
      
 197 
     | 
    
         
            +
                            name: "header off by one",
         
     | 
| 
      
 198 
     | 
    
         
            +
                            input: TestEntry {
         
     | 
| 
      
 199 
     | 
    
         
            +
                                header: 4,
         
     | 
| 
      
 200 
     | 
    
         
            +
                                json: "123",
         
     | 
| 
      
 201 
     | 
    
         
            +
                                padding_len: 1,
         
     | 
| 
      
 202 
     | 
    
         
            +
                                value: 1.0,
         
     | 
| 
      
 203 
     | 
    
         
            +
                            },
         
     | 
| 
      
 204 
     | 
    
         
            +
                            expected_err: Some(MmapError::out_of_bounds(24, 16)),
         
     | 
| 
      
 205 
     | 
    
         
            +
                            ..Default::default()
         
     | 
| 
      
 206 
     | 
    
         
            +
                        },
         
     | 
| 
      
 207 
     | 
    
         
            +
                    ];
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                    for case in tc {
         
     | 
| 
      
 210 
     | 
    
         
            +
                        let name = case.name;
         
     | 
| 
      
 211 
     | 
    
         
            +
                        let input = case.input.as_bstring();
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                        let resp = RawEntry::from_slice(&input);
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                        if case.expected_err.is_none() {
         
     | 
| 
      
 216 
     | 
    
         
            +
                            let expected_buf = case.input.as_bytes_no_header();
         
     | 
| 
      
 217 
     | 
    
         
            +
                            let resp = resp.as_ref().unwrap();
         
     | 
| 
      
 218 
     | 
    
         
            +
                            let bytes = resp.bytes;
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                            assert_eq!(expected_buf, bytes.as_bstr(), "test case: {name} - bytes",);
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 223 
     | 
    
         
            +
                                resp.json(),
         
     | 
| 
      
 224 
     | 
    
         
            +
                                case.input.json.as_bytes(),
         
     | 
| 
      
 225 
     | 
    
         
            +
                                "test case: {name} - json matches"
         
     | 
| 
      
 226 
     | 
    
         
            +
                            );
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 229 
     | 
    
         
            +
                                resp.total_len(),
         
     | 
| 
      
 230 
     | 
    
         
            +
                                case.input.as_bstring().len(),
         
     | 
| 
      
 231 
     | 
    
         
            +
                                "test case: {name} - total_len matches"
         
     | 
| 
      
 232 
     | 
    
         
            +
                            );
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 235 
     | 
    
         
            +
                                resp.encoded_len(),
         
     | 
| 
      
 236 
     | 
    
         
            +
                                case.input.json.len(),
         
     | 
| 
      
 237 
     | 
    
         
            +
                                "test case: {name} - encoded_len matches"
         
     | 
| 
      
 238 
     | 
    
         
            +
                            );
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                            assert!(
         
     | 
| 
      
 241 
     | 
    
         
            +
                                resp.json().iter().all(|&c| c != b' '),
         
     | 
| 
      
 242 
     | 
    
         
            +
                                "test case: {name} - no spaces in json"
         
     | 
| 
      
 243 
     | 
    
         
            +
                            );
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                            let padding_len = RawEntry::padding_len(case.input.json.len());
         
     | 
| 
      
 246 
     | 
    
         
            +
                            assert!(
         
     | 
| 
      
 247 
     | 
    
         
            +
                                bytes[resp.encoded_len..resp.encoded_len + padding_len]
         
     | 
| 
      
 248 
     | 
    
         
            +
                                    .iter()
         
     | 
| 
      
 249 
     | 
    
         
            +
                                    .all(|&c| c == b' '),
         
     | 
| 
      
 250 
     | 
    
         
            +
                                "test case: {name} - padding is spaces"
         
     | 
| 
      
 251 
     | 
    
         
            +
                            );
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 254 
     | 
    
         
            +
                                resp.value(),
         
     | 
| 
      
 255 
     | 
    
         
            +
                                case.input.value,
         
     | 
| 
      
 256 
     | 
    
         
            +
                                "test case: {name} - value is correct"
         
     | 
| 
      
 257 
     | 
    
         
            +
                            );
         
     | 
| 
      
 258 
     | 
    
         
            +
                        }
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
                        if let Some(expected_enc_len) = case.expected_enc_len {
         
     | 
| 
      
 261 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 262 
     | 
    
         
            +
                                expected_enc_len,
         
     | 
| 
      
 263 
     | 
    
         
            +
                                resp.as_ref().unwrap().encoded_len,
         
     | 
| 
      
 264 
     | 
    
         
            +
                                "test case: {name} - encoded len",
         
     | 
| 
      
 265 
     | 
    
         
            +
                            );
         
     | 
| 
      
 266 
     | 
    
         
            +
                        }
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                        if let Some(expected_err) = case.expected_err {
         
     | 
| 
      
 269 
     | 
    
         
            +
                            assert_eq!(expected_err, resp.unwrap_err(), "test case: {name} - error",);
         
     | 
| 
      
 270 
     | 
    
         
            +
                        }
         
     | 
| 
      
 271 
     | 
    
         
            +
                    }
         
     | 
| 
      
 272 
     | 
    
         
            +
                }
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
      
 274 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 275 
     | 
    
         
            +
                fn test_save() {
         
     | 
| 
      
 276 
     | 
    
         
            +
                    struct TestCase {
         
     | 
| 
      
 277 
     | 
    
         
            +
                        name: &'static str,
         
     | 
| 
      
 278 
     | 
    
         
            +
                        key: &'static [u8],
         
     | 
| 
      
 279 
     | 
    
         
            +
                        value: f64,
         
     | 
| 
      
 280 
     | 
    
         
            +
                        buf_len: usize,
         
     | 
| 
      
 281 
     | 
    
         
            +
                        expected_entry: Option<TestEntry>,
         
     | 
| 
      
 282 
     | 
    
         
            +
                        expected_resp: Result<usize>,
         
     | 
| 
      
 283 
     | 
    
         
            +
                    }
         
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
      
 285 
     | 
    
         
            +
                    // TODO No test case to validate keys with len > i32::MAX, adding a static that large crashes
         
     | 
| 
      
 286 
     | 
    
         
            +
                    // the test binary.
         
     | 
| 
      
 287 
     | 
    
         
            +
                    let tc = vec![
         
     | 
| 
      
 288 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 289 
     | 
    
         
            +
                            name: "ok",
         
     | 
| 
      
 290 
     | 
    
         
            +
                            key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 291 
     | 
    
         
            +
                            value: 256.0,
         
     | 
| 
      
 292 
     | 
    
         
            +
                            buf_len: 256,
         
     | 
| 
      
 293 
     | 
    
         
            +
                            expected_entry: Some(TestEntry {
         
     | 
| 
      
 294 
     | 
    
         
            +
                                header: 61,
         
     | 
| 
      
 295 
     | 
    
         
            +
                                json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 296 
     | 
    
         
            +
                                padding_len: 7,
         
     | 
| 
      
 297 
     | 
    
         
            +
                                value: 256.0,
         
     | 
| 
      
 298 
     | 
    
         
            +
                            }),
         
     | 
| 
      
 299 
     | 
    
         
            +
                            expected_resp: Ok(72),
         
     | 
| 
      
 300 
     | 
    
         
            +
                        },
         
     | 
| 
      
 301 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 302 
     | 
    
         
            +
                            name: "zero length key",
         
     | 
| 
      
 303 
     | 
    
         
            +
                            key: b"",
         
     | 
| 
      
 304 
     | 
    
         
            +
                            value: 1.0,
         
     | 
| 
      
 305 
     | 
    
         
            +
                            buf_len: 256,
         
     | 
| 
      
 306 
     | 
    
         
            +
                            expected_entry: Some(TestEntry {
         
     | 
| 
      
 307 
     | 
    
         
            +
                                header: 0,
         
     | 
| 
      
 308 
     | 
    
         
            +
                                json: "",
         
     | 
| 
      
 309 
     | 
    
         
            +
                                padding_len: 4,
         
     | 
| 
      
 310 
     | 
    
         
            +
                                value: 1.0,
         
     | 
| 
      
 311 
     | 
    
         
            +
                            }),
         
     | 
| 
      
 312 
     | 
    
         
            +
                            expected_resp: Ok(8),
         
     | 
| 
      
 313 
     | 
    
         
            +
                        },
         
     | 
| 
      
 314 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 315 
     | 
    
         
            +
                            name: "infinite value",
         
     | 
| 
      
 316 
     | 
    
         
            +
                            key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 317 
     | 
    
         
            +
                            value: f64::INFINITY,
         
     | 
| 
      
 318 
     | 
    
         
            +
                            buf_len: 256,
         
     | 
| 
      
 319 
     | 
    
         
            +
                            expected_entry: Some(TestEntry {
         
     | 
| 
      
 320 
     | 
    
         
            +
                                header: 61,
         
     | 
| 
      
 321 
     | 
    
         
            +
                                json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 322 
     | 
    
         
            +
                                padding_len: 7,
         
     | 
| 
      
 323 
     | 
    
         
            +
                                value: f64::INFINITY,
         
     | 
| 
      
 324 
     | 
    
         
            +
                            }),
         
     | 
| 
      
 325 
     | 
    
         
            +
                            expected_resp: Ok(72),
         
     | 
| 
      
 326 
     | 
    
         
            +
                        },
         
     | 
| 
      
 327 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 328 
     | 
    
         
            +
                            name: "buf len matches entry len",
         
     | 
| 
      
 329 
     | 
    
         
            +
                            key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 330 
     | 
    
         
            +
                            value: 1.0,
         
     | 
| 
      
 331 
     | 
    
         
            +
                            buf_len: 80,
         
     | 
| 
      
 332 
     | 
    
         
            +
                            expected_entry: Some(TestEntry {
         
     | 
| 
      
 333 
     | 
    
         
            +
                                header: 61,
         
     | 
| 
      
 334 
     | 
    
         
            +
                                json: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 335 
     | 
    
         
            +
                                padding_len: 7,
         
     | 
| 
      
 336 
     | 
    
         
            +
                                value: 1.0,
         
     | 
| 
      
 337 
     | 
    
         
            +
                            }),
         
     | 
| 
      
 338 
     | 
    
         
            +
                            expected_resp: Ok(72),
         
     | 
| 
      
 339 
     | 
    
         
            +
                        },
         
     | 
| 
      
 340 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 341 
     | 
    
         
            +
                            name: "buf much too short",
         
     | 
| 
      
 342 
     | 
    
         
            +
                            key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 343 
     | 
    
         
            +
                            value: 1.0,
         
     | 
| 
      
 344 
     | 
    
         
            +
                            buf_len: 5,
         
     | 
| 
      
 345 
     | 
    
         
            +
                            expected_entry: None,
         
     | 
| 
      
 346 
     | 
    
         
            +
                            expected_resp: Err(MmapError::Other(format!(
         
     | 
| 
      
 347 
     | 
    
         
            +
                                "entry length {} larger than slice length {}",
         
     | 
| 
      
 348 
     | 
    
         
            +
                                80, 5,
         
     | 
| 
      
 349 
     | 
    
         
            +
                            ))),
         
     | 
| 
      
 350 
     | 
    
         
            +
                        },
         
     | 
| 
      
 351 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 352 
     | 
    
         
            +
                            name: "buf short by one",
         
     | 
| 
      
 353 
     | 
    
         
            +
                            key: br#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
         
     | 
| 
      
 354 
     | 
    
         
            +
                            value: 1.0,
         
     | 
| 
      
 355 
     | 
    
         
            +
                            buf_len: 79,
         
     | 
| 
      
 356 
     | 
    
         
            +
                            expected_entry: None,
         
     | 
| 
      
 357 
     | 
    
         
            +
                            expected_resp: Err(MmapError::Other(format!(
         
     | 
| 
      
 358 
     | 
    
         
            +
                                "entry length {} larger than slice length {}",
         
     | 
| 
      
 359 
     | 
    
         
            +
                                80, 79,
         
     | 
| 
      
 360 
     | 
    
         
            +
                            ))),
         
     | 
| 
      
 361 
     | 
    
         
            +
                        },
         
     | 
| 
      
 362 
     | 
    
         
            +
                    ];
         
     | 
| 
      
 363 
     | 
    
         
            +
             
     | 
| 
      
 364 
     | 
    
         
            +
                    for case in tc {
         
     | 
| 
      
 365 
     | 
    
         
            +
                        let mut buf = vec![0; case.buf_len];
         
     | 
| 
      
 366 
     | 
    
         
            +
                        let resp = RawEntry::save(&mut buf, case.key, case.value);
         
     | 
| 
      
 367 
     | 
    
         
            +
             
     | 
| 
      
 368 
     | 
    
         
            +
                        assert_eq!(
         
     | 
| 
      
 369 
     | 
    
         
            +
                            case.expected_resp, resp,
         
     | 
| 
      
 370 
     | 
    
         
            +
                            "test case: {} - response",
         
     | 
| 
      
 371 
     | 
    
         
            +
                            case.name,
         
     | 
| 
      
 372 
     | 
    
         
            +
                        );
         
     | 
| 
      
 373 
     | 
    
         
            +
             
     | 
| 
      
 374 
     | 
    
         
            +
                        if let Some(e) = case.expected_entry {
         
     | 
| 
      
 375 
     | 
    
         
            +
                            let expected_buf = e.as_bstring();
         
     | 
| 
      
 376 
     | 
    
         
            +
             
     | 
| 
      
 377 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 378 
     | 
    
         
            +
                                expected_buf,
         
     | 
| 
      
 379 
     | 
    
         
            +
                                buf[..expected_buf.len()].as_bstr(),
         
     | 
| 
      
 380 
     | 
    
         
            +
                                "test case: {} - buffer state",
         
     | 
| 
      
 381 
     | 
    
         
            +
                                case.name
         
     | 
| 
      
 382 
     | 
    
         
            +
                            );
         
     | 
| 
      
 383 
     | 
    
         
            +
             
     | 
| 
      
 384 
     | 
    
         
            +
                            let header_len = u32::from_ne_bytes(buf[..size_of::<u32>()].try_into().unwrap());
         
     | 
| 
      
 385 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 386 
     | 
    
         
            +
                                case.key.len(),
         
     | 
| 
      
 387 
     | 
    
         
            +
                                header_len as usize,
         
     | 
| 
      
 388 
     | 
    
         
            +
                                "test case: {} - size header",
         
     | 
| 
      
 389 
     | 
    
         
            +
                                case.name,
         
     | 
| 
      
 390 
     | 
    
         
            +
                            );
         
     | 
| 
      
 391 
     | 
    
         
            +
                        }
         
     | 
| 
      
 392 
     | 
    
         
            +
                    }
         
     | 
| 
      
 393 
     | 
    
         
            +
                }
         
     | 
| 
      
 394 
     | 
    
         
            +
             
     | 
| 
      
 395 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 396 
     | 
    
         
            +
                fn test_calc_value_offset() {
         
     | 
| 
      
 397 
     | 
    
         
            +
                    struct TestCase {
         
     | 
| 
      
 398 
     | 
    
         
            +
                        name: &'static str,
         
     | 
| 
      
 399 
     | 
    
         
            +
                        encoded_len: usize,
         
     | 
| 
      
 400 
     | 
    
         
            +
                        expected_value_offset: Option<usize>,
         
     | 
| 
      
 401 
     | 
    
         
            +
                        expected_total_len: Option<usize>,
         
     | 
| 
      
 402 
     | 
    
         
            +
                        expected_err: Option<MmapError>,
         
     | 
| 
      
 403 
     | 
    
         
            +
                    }
         
     | 
| 
      
 404 
     | 
    
         
            +
             
     | 
| 
      
 405 
     | 
    
         
            +
                    let tc = vec![
         
     | 
| 
      
 406 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 407 
     | 
    
         
            +
                            name: "ok",
         
     | 
| 
      
 408 
     | 
    
         
            +
                            encoded_len: 8,
         
     | 
| 
      
 409 
     | 
    
         
            +
                            expected_value_offset: Some(16),
         
     | 
| 
      
 410 
     | 
    
         
            +
                            expected_total_len: Some(24),
         
     | 
| 
      
 411 
     | 
    
         
            +
                            expected_err: None,
         
     | 
| 
      
 412 
     | 
    
         
            +
                        },
         
     | 
| 
      
 413 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 414 
     | 
    
         
            +
                            name: "padding length one",
         
     | 
| 
      
 415 
     | 
    
         
            +
                            encoded_len: 3,
         
     | 
| 
      
 416 
     | 
    
         
            +
                            expected_value_offset: Some(8),
         
     | 
| 
      
 417 
     | 
    
         
            +
                            expected_total_len: Some(16),
         
     | 
| 
      
 418 
     | 
    
         
            +
                            expected_err: None,
         
     | 
| 
      
 419 
     | 
    
         
            +
                        },
         
     | 
| 
      
 420 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 421 
     | 
    
         
            +
                            name: "padding length eight",
         
     | 
| 
      
 422 
     | 
    
         
            +
                            encoded_len: 4,
         
     | 
| 
      
 423 
     | 
    
         
            +
                            expected_value_offset: Some(16),
         
     | 
| 
      
 424 
     | 
    
         
            +
                            expected_total_len: Some(24),
         
     | 
| 
      
 425 
     | 
    
         
            +
                            expected_err: None,
         
     | 
| 
      
 426 
     | 
    
         
            +
                        },
         
     | 
| 
      
 427 
     | 
    
         
            +
                        TestCase {
         
     | 
| 
      
 428 
     | 
    
         
            +
                            name: "encoded len gt i32::MAX",
         
     | 
| 
      
 429 
     | 
    
         
            +
                            encoded_len: i32::MAX as usize + 1,
         
     | 
| 
      
 430 
     | 
    
         
            +
                            expected_value_offset: None,
         
     | 
| 
      
 431 
     | 
    
         
            +
                            expected_total_len: None,
         
     | 
| 
      
 432 
     | 
    
         
            +
                            expected_err: Some(MmapError::KeyLength),
         
     | 
| 
      
 433 
     | 
    
         
            +
                        },
         
     | 
| 
      
 434 
     | 
    
         
            +
                    ];
         
     | 
| 
      
 435 
     | 
    
         
            +
             
     | 
| 
      
 436 
     | 
    
         
            +
                    for case in tc {
         
     | 
| 
      
 437 
     | 
    
         
            +
                        let name = case.name;
         
     | 
| 
      
 438 
     | 
    
         
            +
                        if let Some(expected_value_offset) = case.expected_value_offset {
         
     | 
| 
      
 439 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 440 
     | 
    
         
            +
                                expected_value_offset,
         
     | 
| 
      
 441 
     | 
    
         
            +
                                RawEntry::calc_value_offset(case.encoded_len).unwrap(),
         
     | 
| 
      
 442 
     | 
    
         
            +
                                "test case: {name} - value offset"
         
     | 
| 
      
 443 
     | 
    
         
            +
                            );
         
     | 
| 
      
 444 
     | 
    
         
            +
                        }
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
                        if let Some(expected_total_len) = case.expected_total_len {
         
     | 
| 
      
 447 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 448 
     | 
    
         
            +
                                expected_total_len,
         
     | 
| 
      
 449 
     | 
    
         
            +
                                RawEntry::calc_total_len(case.encoded_len).unwrap(),
         
     | 
| 
      
 450 
     | 
    
         
            +
                                "test case: {name} - total len"
         
     | 
| 
      
 451 
     | 
    
         
            +
                            );
         
     | 
| 
      
 452 
     | 
    
         
            +
                        }
         
     | 
| 
      
 453 
     | 
    
         
            +
             
     | 
| 
      
 454 
     | 
    
         
            +
                        if let Some(expected_err) = case.expected_err {
         
     | 
| 
      
 455 
     | 
    
         
            +
                            assert_eq!(
         
     | 
| 
      
 456 
     | 
    
         
            +
                                expected_err,
         
     | 
| 
      
 457 
     | 
    
         
            +
                                RawEntry::calc_value_offset(case.encoded_len).unwrap_err(),
         
     | 
| 
      
 458 
     | 
    
         
            +
                                "test case: {name} - err"
         
     | 
| 
      
 459 
     | 
    
         
            +
                            );
         
     | 
| 
      
 460 
     | 
    
         
            +
                        }
         
     | 
| 
      
 461 
     | 
    
         
            +
                    }
         
     | 
| 
      
 462 
     | 
    
         
            +
                }
         
     | 
| 
      
 463 
     | 
    
         
            +
             
     | 
| 
      
 464 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 465 
     | 
    
         
            +
                fn test_padding_len() {
         
     | 
| 
      
 466 
     | 
    
         
            +
                    for encoded_len in 0..64 {
         
     | 
| 
      
 467 
     | 
    
         
            +
                        let padding = RawEntry::padding_len(encoded_len);
         
     | 
| 
      
 468 
     | 
    
         
            +
             
     | 
| 
      
 469 
     | 
    
         
            +
                        // Validate we're actually aligning to 8 bytes.
         
     | 
| 
      
 470 
     | 
    
         
            +
                        assert!((size_of::<u32>() + encoded_len + padding) % 8 == 0)
         
     | 
| 
      
 471 
     | 
    
         
            +
                    }
         
     | 
| 
      
 472 
     | 
    
         
            +
                }
         
     | 
| 
      
 473 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,222 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            use bstr::{BString, B};
         
     | 
| 
      
 2 
     | 
    
         
            +
            use std::fs::File;
         
     | 
| 
      
 3 
     | 
    
         
            +
            use std::io::{Read, Seek, Write};
         
     | 
| 
      
 4 
     | 
    
         
            +
            use std::path::PathBuf;
         
     | 
| 
      
 5 
     | 
    
         
            +
            use tempfile::{tempdir, TempDir};
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            use crate::raw_entry::RawEntry;
         
     | 
| 
      
 8 
     | 
    
         
            +
            use crate::HEADER_SIZE;
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            #[derive(PartialEq, Default, Debug)]
         
     | 
| 
      
 11 
     | 
    
         
            +
            pub struct TestEntry {
         
     | 
| 
      
 12 
     | 
    
         
            +
                pub header: u32,
         
     | 
| 
      
 13 
     | 
    
         
            +
                pub json: &'static str,
         
     | 
| 
      
 14 
     | 
    
         
            +
                pub padding_len: usize,
         
     | 
| 
      
 15 
     | 
    
         
            +
                pub value: f64,
         
     | 
| 
      
 16 
     | 
    
         
            +
            }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            impl TestEntry {
         
     | 
| 
      
 19 
     | 
    
         
            +
                pub fn new(json: &'static str, value: f64) -> Self {
         
     | 
| 
      
 20 
     | 
    
         
            +
                    TestEntry {
         
     | 
| 
      
 21 
     | 
    
         
            +
                        header: json.len() as u32,
         
     | 
| 
      
 22 
     | 
    
         
            +
                        json,
         
     | 
| 
      
 23 
     | 
    
         
            +
                        padding_len: RawEntry::padding_len(json.len()),
         
     | 
| 
      
 24 
     | 
    
         
            +
                        value,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    }
         
     | 
| 
      
 26 
     | 
    
         
            +
                }
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                pub fn as_bytes(&self) -> Vec<u8> {
         
     | 
| 
      
 29 
     | 
    
         
            +
                    [
         
     | 
| 
      
 30 
     | 
    
         
            +
                        B(&self.header.to_ne_bytes()),
         
     | 
| 
      
 31 
     | 
    
         
            +
                        self.json.as_bytes(),
         
     | 
| 
      
 32 
     | 
    
         
            +
                        &vec![b' '; self.padding_len],
         
     | 
| 
      
 33 
     | 
    
         
            +
                        B(&self.value.to_ne_bytes()),
         
     | 
| 
      
 34 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 35 
     | 
    
         
            +
                    .concat()
         
     | 
| 
      
 36 
     | 
    
         
            +
                }
         
     | 
| 
      
 37 
     | 
    
         
            +
                pub fn as_bstring(&self) -> BString {
         
     | 
| 
      
 38 
     | 
    
         
            +
                    [
         
     | 
| 
      
 39 
     | 
    
         
            +
                        B(&self.header.to_ne_bytes()),
         
     | 
| 
      
 40 
     | 
    
         
            +
                        self.json.as_bytes(),
         
     | 
| 
      
 41 
     | 
    
         
            +
                        &vec![b' '; self.padding_len],
         
     | 
| 
      
 42 
     | 
    
         
            +
                        B(&self.value.to_ne_bytes()),
         
     | 
| 
      
 43 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 44 
     | 
    
         
            +
                    .concat()
         
     | 
| 
      
 45 
     | 
    
         
            +
                    .into()
         
     | 
| 
      
 46 
     | 
    
         
            +
                }
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                pub fn as_bytes_no_header(&self) -> BString {
         
     | 
| 
      
 49 
     | 
    
         
            +
                    [
         
     | 
| 
      
 50 
     | 
    
         
            +
                        self.json.as_bytes(),
         
     | 
| 
      
 51 
     | 
    
         
            +
                        &vec![b' '; self.padding_len],
         
     | 
| 
      
 52 
     | 
    
         
            +
                        B(&self.value.to_ne_bytes()),
         
     | 
| 
      
 53 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 54 
     | 
    
         
            +
                    .concat()
         
     | 
| 
      
 55 
     | 
    
         
            +
                    .into()
         
     | 
| 
      
 56 
     | 
    
         
            +
                }
         
     | 
| 
      
 57 
     | 
    
         
            +
            }
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            /// Format the data for a `.db` file.
         
     | 
| 
      
 60 
     | 
    
         
            +
            /// Optional header value can be used to set an invalid `used` size.
         
     | 
| 
      
 61 
     | 
    
         
            +
            pub fn entries_to_db(entries: &[&'static str], values: &[f64], header: Option<u32>) -> Vec<u8> {
         
     | 
| 
      
 62 
     | 
    
         
            +
                let mut out = Vec::new();
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                let entry_bytes: Vec<_> = entries
         
     | 
| 
      
 65 
     | 
    
         
            +
                    .iter()
         
     | 
| 
      
 66 
     | 
    
         
            +
                    .zip(values)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    .flat_map(|(e, val)| TestEntry::new(e, *val).as_bytes())
         
     | 
| 
      
 68 
     | 
    
         
            +
                    .collect();
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                let used = match header {
         
     | 
| 
      
 71 
     | 
    
         
            +
                    Some(u) => u,
         
     | 
| 
      
 72 
     | 
    
         
            +
                    None => (entry_bytes.len() + HEADER_SIZE) as u32,
         
     | 
| 
      
 73 
     | 
    
         
            +
                };
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                out.extend(used.to_ne_bytes());
         
     | 
| 
      
 76 
     | 
    
         
            +
                out.extend(&[0x0u8; 4]); // Padding.
         
     | 
| 
      
 77 
     | 
    
         
            +
                out.extend(entry_bytes);
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                out
         
     | 
| 
      
 80 
     | 
    
         
            +
            }
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
            /// A temporary file, path, and dir for use with testing.
         
     | 
| 
      
 83 
     | 
    
         
            +
            #[derive(Debug)]
         
     | 
| 
      
 84 
     | 
    
         
            +
            pub struct TestFile {
         
     | 
| 
      
 85 
     | 
    
         
            +
                pub file: File,
         
     | 
| 
      
 86 
     | 
    
         
            +
                pub path: PathBuf,
         
     | 
| 
      
 87 
     | 
    
         
            +
                pub dir: TempDir,
         
     | 
| 
      
 88 
     | 
    
         
            +
            }
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            impl TestFile {
         
     | 
| 
      
 91 
     | 
    
         
            +
                pub fn new(file_data: &[u8]) -> TestFile {
         
     | 
| 
      
 92 
     | 
    
         
            +
                    let dir = tempdir().unwrap();
         
     | 
| 
      
 93 
     | 
    
         
            +
                    let path = dir.path().join("test.db");
         
     | 
| 
      
 94 
     | 
    
         
            +
                    let mut file = File::options()
         
     | 
| 
      
 95 
     | 
    
         
            +
                        .create(true)
         
     | 
| 
      
 96 
     | 
    
         
            +
                        .read(true)
         
     | 
| 
      
 97 
     | 
    
         
            +
                        .write(true)
         
     | 
| 
      
 98 
     | 
    
         
            +
                        .open(&path)
         
     | 
| 
      
 99 
     | 
    
         
            +
                        .unwrap();
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    file.write_all(file_data).unwrap();
         
     | 
| 
      
 102 
     | 
    
         
            +
                    file.sync_all().unwrap();
         
     | 
| 
      
 103 
     | 
    
         
            +
                    file.rewind().unwrap();
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    // We need to keep `dir` in scope so it doesn't drop before the files it
         
     | 
| 
      
 106 
     | 
    
         
            +
                    // contains, which may prevent cleanup.
         
     | 
| 
      
 107 
     | 
    
         
            +
                    TestFile { file, path, dir }
         
     | 
| 
      
 108 
     | 
    
         
            +
                }
         
     | 
| 
      
 109 
     | 
    
         
            +
            }
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
            mod test {
         
     | 
| 
      
 112 
     | 
    
         
            +
                use super::*;
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 115 
     | 
    
         
            +
                fn test_entry_new() {
         
     | 
| 
      
 116 
     | 
    
         
            +
                    let json = "foobar";
         
     | 
| 
      
 117 
     | 
    
         
            +
                    let value = 1.0f64;
         
     | 
| 
      
 118 
     | 
    
         
            +
                    let expected = TestEntry {
         
     | 
| 
      
 119 
     | 
    
         
            +
                        header: 6,
         
     | 
| 
      
 120 
     | 
    
         
            +
                        json,
         
     | 
| 
      
 121 
     | 
    
         
            +
                        padding_len: 6,
         
     | 
| 
      
 122 
     | 
    
         
            +
                        value,
         
     | 
| 
      
 123 
     | 
    
         
            +
                    };
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    let actual = TestEntry::new(json, value);
         
     | 
| 
      
 126 
     | 
    
         
            +
                    assert_eq!(expected, actual);
         
     | 
| 
      
 127 
     | 
    
         
            +
                }
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 130 
     | 
    
         
            +
                fn test_entry_bytes() {
         
     | 
| 
      
 131 
     | 
    
         
            +
                    let json = "foobar";
         
     | 
| 
      
 132 
     | 
    
         
            +
                    let value = 1.0f64;
         
     | 
| 
      
 133 
     | 
    
         
            +
                    let expected = [
         
     | 
| 
      
 134 
     | 
    
         
            +
                        &6u32.to_ne_bytes(),
         
     | 
| 
      
 135 
     | 
    
         
            +
                        B(json),
         
     | 
| 
      
 136 
     | 
    
         
            +
                        &[b' '; 6],
         
     | 
| 
      
 137 
     | 
    
         
            +
                        &value.to_ne_bytes(),
         
     | 
| 
      
 138 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 139 
     | 
    
         
            +
                    .concat();
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    let actual = TestEntry::new(json, value).as_bstring();
         
     | 
| 
      
 142 
     | 
    
         
            +
                    assert_eq!(expected, actual);
         
     | 
| 
      
 143 
     | 
    
         
            +
                }
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 146 
     | 
    
         
            +
                fn test_entry_bytes_no_header() {
         
     | 
| 
      
 147 
     | 
    
         
            +
                    let json = "foobar";
         
     | 
| 
      
 148 
     | 
    
         
            +
                    let value = 1.0f64;
         
     | 
| 
      
 149 
     | 
    
         
            +
                    let expected = [B(json), &[b' '; 6], &value.to_ne_bytes()].concat();
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    let actual = TestEntry::new(json, value).as_bytes_no_header();
         
     | 
| 
      
 152 
     | 
    
         
            +
                    assert_eq!(expected, actual);
         
     | 
| 
      
 153 
     | 
    
         
            +
                }
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 156 
     | 
    
         
            +
                fn test_entries_to_db_header_correct() {
         
     | 
| 
      
 157 
     | 
    
         
            +
                    let json = &["foobar", "qux"];
         
     | 
| 
      
 158 
     | 
    
         
            +
                    let values = &[1.0, 2.0];
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                    let out = entries_to_db(json, values, None);
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                    assert_eq!(48u32.to_ne_bytes(), out[0..4], "used set correctly");
         
     | 
| 
      
 163 
     | 
    
         
            +
                    assert_eq!([0u8; 4], out[4..8], "padding set");
         
     | 
| 
      
 164 
     | 
    
         
            +
                    assert_eq!(
         
     | 
| 
      
 165 
     | 
    
         
            +
                        TestEntry::new(json[0], values[0]).as_bytes(),
         
     | 
| 
      
 166 
     | 
    
         
            +
                        out[8..32],
         
     | 
| 
      
 167 
     | 
    
         
            +
                        "first entry matches"
         
     | 
| 
      
 168 
     | 
    
         
            +
                    );
         
     | 
| 
      
 169 
     | 
    
         
            +
                    assert_eq!(
         
     | 
| 
      
 170 
     | 
    
         
            +
                        TestEntry::new(json[1], values[1]).as_bytes(),
         
     | 
| 
      
 171 
     | 
    
         
            +
                        out[32..48],
         
     | 
| 
      
 172 
     | 
    
         
            +
                        "second entry matches"
         
     | 
| 
      
 173 
     | 
    
         
            +
                    );
         
     | 
| 
      
 174 
     | 
    
         
            +
                }
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 177 
     | 
    
         
            +
                fn test_entries_to_db_header_wrong() {
         
     | 
| 
      
 178 
     | 
    
         
            +
                    let json = &["foobar", "qux"];
         
     | 
| 
      
 179 
     | 
    
         
            +
                    let values = &[1.0, 2.0];
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    const WRONG_USED: u32 = 1000;
         
     | 
| 
      
 182 
     | 
    
         
            +
                    let out = entries_to_db(json, values, Some(WRONG_USED));
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                    assert_eq!(
         
     | 
| 
      
 185 
     | 
    
         
            +
                        WRONG_USED.to_ne_bytes(),
         
     | 
| 
      
 186 
     | 
    
         
            +
                        out[0..4],
         
     | 
| 
      
 187 
     | 
    
         
            +
                        "used set to value requested"
         
     | 
| 
      
 188 
     | 
    
         
            +
                    );
         
     | 
| 
      
 189 
     | 
    
         
            +
                    assert_eq!([0u8; 4], out[4..8], "padding set");
         
     | 
| 
      
 190 
     | 
    
         
            +
                    assert_eq!(
         
     | 
| 
      
 191 
     | 
    
         
            +
                        TestEntry::new(json[0], values[0]).as_bytes(),
         
     | 
| 
      
 192 
     | 
    
         
            +
                        out[8..32],
         
     | 
| 
      
 193 
     | 
    
         
            +
                        "first entry matches"
         
     | 
| 
      
 194 
     | 
    
         
            +
                    );
         
     | 
| 
      
 195 
     | 
    
         
            +
                    assert_eq!(
         
     | 
| 
      
 196 
     | 
    
         
            +
                        TestEntry::new(json[1], values[1]).as_bytes(),
         
     | 
| 
      
 197 
     | 
    
         
            +
                        out[32..48],
         
     | 
| 
      
 198 
     | 
    
         
            +
                        "second entry matches"
         
     | 
| 
      
 199 
     | 
    
         
            +
                    );
         
     | 
| 
      
 200 
     | 
    
         
            +
                }
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 203 
     | 
    
         
            +
                fn test_file() {
         
     | 
| 
      
 204 
     | 
    
         
            +
                    let mut test_file = TestFile::new(b"foobar");
         
     | 
| 
      
 205 
     | 
    
         
            +
                    let stat = test_file.file.metadata().unwrap();
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                    assert_eq!(6, stat.len(), "file length");
         
     | 
| 
      
 208 
     | 
    
         
            +
                    assert_eq!(
         
     | 
| 
      
 209 
     | 
    
         
            +
                        0,
         
     | 
| 
      
 210 
     | 
    
         
            +
                        test_file.file.stream_position().unwrap(),
         
     | 
| 
      
 211 
     | 
    
         
            +
                        "at start of file"
         
     | 
| 
      
 212 
     | 
    
         
            +
                    );
         
     | 
| 
      
 213 
     | 
    
         
            +
                    let mut out_buf = vec![0u8; 256];
         
     | 
| 
      
 214 
     | 
    
         
            +
                    let read_result = test_file.file.read(&mut out_buf);
         
     | 
| 
      
 215 
     | 
    
         
            +
                    assert!(read_result.is_ok());
         
     | 
| 
      
 216 
     | 
    
         
            +
                    assert_eq!(6, read_result.unwrap(), "file is readable");
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
                    let write_result = test_file.file.write(b"qux");
         
     | 
| 
      
 219 
     | 
    
         
            +
                    assert!(write_result.is_ok());
         
     | 
| 
      
 220 
     | 
    
         
            +
                    assert_eq!(3, write_result.unwrap(), "file is writable");
         
     | 
| 
      
 221 
     | 
    
         
            +
                }
         
     | 
| 
      
 222 
     | 
    
         
            +
            }
         
     |