fn raw_may_allocate_memory()

in bindings/rust/extended/s2n-tls/src/fingerprint.rs [583:672]


    fn raw_may_allocate_memory() -> Result<(), Box<dyn Error>> {
        let client_hello = ClientHello::parse_client_hello(CLIENT_HELLO_BYTES)?;

        let mut builder = Fingerprint::builder(FingerprintType::JA3)?;

        let minimum_size = JA3_FULL_STRING.len();
        let large_size = minimum_size + 100;

        // If we want to allocate the known value of the raw string,
        // then that allocation must happen when raw() is called rather than
        // when the fingerprint is built.
        let snapshot = checkers::with(|| {
            let mut fingerprint = builder.build(&client_hello).unwrap();
            // Calculating the hash is necessary to calculate the raw_size,
            // But the hash_does_not_allocate_memory test proves this does not
            // allocate any memory.
            fingerprint.hash().unwrap();

            fingerprint.raw().unwrap();
        });
        // Expect a single allocation to allocate the raw string
        assert_eq!(snapshot.events.allocs(), 1);
        assert_eq!(snapshot.events.reallocs(), 0);
        assert_eq!(snapshot.events.frees(), 0);
        assert_eq!(snapshot.events.max_memory_used().unwrap(), minimum_size);

        // Snapshots must be cumulative to accurately track total allocations,
        // so keep track of the current snapshot.
        let mut full_snapshot = snapshot;

        // Expect that repeating either the build or the calculation does not
        // lead to any new allocations.
        let snapshot = checkers::with(|| {
            for _ in 0..10 {
                let mut fingerprint = builder.build(&client_hello).unwrap();
                // Calculating the hash is necessary to calculate the raw_size,
                // But the hash_does_not_allocate_memory test proves this does not
                // allocate any memory.
                fingerprint.hash().unwrap();

                for _ in 0..10 {
                    fingerprint.raw().unwrap();
                }
            }
        });
        assert!(snapshot.events.is_empty());

        // If we set a larger raw size on the builder,
        // then the raw string is reallocated.
        let snapshot = checkers::with(|| {
            builder.set_raw_size(large_size).unwrap();
        });
        assert_eq!(snapshot.events.allocs(), 0);
        assert_eq!(snapshot.events.reallocs(), 1);
        assert_eq!(snapshot.events.frees(), 0);

        // Snapshots must be cumulative to accurately track total allocations,
        // so add the new snapshot events.
        for event in snapshot.events.as_slice() {
            full_snapshot.events.push(event.clone());
        }
        // The new total memory should be the larger size we set on the builder.
        assert_eq!(full_snapshot.events.max_memory_used().unwrap(), large_size);

        // If the raw size was set on the builder, then the raw string is not
        // allocated when raw() is called.
        let snapshot = checkers::with(|| {
            let mut fingerprint = builder.build(&client_hello).unwrap();
            fingerprint.raw().unwrap();
        });
        assert!(snapshot.events.is_empty());

        // If we set a smaller raw size on the builder,
        // then the raw string is not reallocated.
        let snapshot = checkers::with(|| {
            builder.set_raw_size(minimum_size).unwrap();
        });
        assert!(snapshot.events.is_empty());

        // Recalculating the raw string does not reallocate the raw string
        let snapshot = checkers::with(|| {
            for _ in 0..10 {
                let mut fingerprint = builder.build(&client_hello).unwrap();
                fingerprint.raw().unwrap();
            }
        });
        assert!(snapshot.events.is_empty());

        Ok(())
    }