<commit_before>pub mod specifiers_iterator;

/// A safe-ish wrapper around `libc::snprintf`.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                // Might fail if `local_format` contains a null byte. TODO decide if this is a good
                // error-handling strategy, or we should report back to user, or panic, or what
                if let Ok(local_format_cstring) = std::ffi::CString::new(local_format) {
                    let buf_size = 1024usize;
                    let buffer: std::vec::Vec<u8> = std::vec::Vec::with_capacity(buf_size);
                    if let Ok(buffer) = std::ffi::CString::new(buffer) {
                        unsafe {
                            let buffer_ptr = buffer.into_raw();
                            // TODO: check how many bytes were written
                            let bytes_written = libc::snprintf(
                                buffer_ptr,
                                buf_size as libc::size_t,
                                local_format_cstring.as_ptr() as *const libc::c_char,
                                $arg);
                            let buffer = std::ffi::CString::from_raw(buffer_ptr);
                            let _bytes_written = bytes_written as usize;
                            if let Ok(formatted) = buffer.into_string() {
                                result.push_str(&formatted);
                            }
                        }
                    }
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_int_x86_64() {
        assert_eq!(fmt!("%i", 42i32), "42");

        let int_min = std::i32::MIN;
        assert_eq!(int_min, -2147483648i32);
        assert_eq!(fmt!("%i", int_min), "-2147483648");

        let int_max = std::i32::MAX;
        assert_eq!(int_max, 2147483647i32);
        assert_eq!(fmt!("%i", int_max), "2147483647");
    }

    #[test]
    fn formats_unsigned_int_x86_64() {
        assert_eq!(fmt!("%u", 42u32), "42");

        assert_eq!(fmt!("%u", 0u32), "0");

        let uint_max = std::u32::MAX;
        assert_eq!(uint_max, 4294967295u32);
        assert_eq!(fmt!("%u", uint_max), "4294967295");
    }

    #[test]
    fn formats_long_int_x86_64() {
        assert_eq!(fmt!("%li", 42i64), "42");

        let int_min = std::i32::MIN as i64;
        let long_min = std::i64::MIN;
        assert!(long_min < int_min);
        assert_eq!(int_min, -2147483648);
        assert_eq!(fmt!("%li", int_min - 1), "-2147483649");

        let int_max = std::i32::MAX as i64;
        let long_max = std::i64::MAX;
        assert!(long_max > int_max);
        assert_eq!(int_max, 2147483647);
        assert_eq!(fmt!("%li", int_max + 1), "2147483648");
    }

    #[test]
    fn formats_long_unsigned_int_x86_64() {
        assert_eq!(fmt!("%lu", 42u64), "42");

        assert_eq!(fmt!("%lu", 0u64), "0");

        let ulong_max = std::u64::MAX;
        assert_eq!(ulong_max, 18446744073709551615u64);
        assert_eq!(fmt!("%lu", ulong_max), "18446744073709551615");
    }

    #[test]
    fn formats_long_long_int_x86_64() {
        assert_eq!(fmt!("%lli", 42i64), "42");

        // This is one bigger than actual std::numeric_limits<long long int>::min()
        // on x86_64. The actual value can't be written in C++ as a literal because both GCC 8 and
        // Clang complain about it being too small to be represented with long long int.
        let input = -9223372036854775807i64;
        let llong_min = std::i64::MIN;
        assert!(llong_min <= input);
        assert_eq!(fmt!("%lli", input), "-9223372036854775807");

        let llong_max = std::i64::MAX;
        assert_eq!(llong_max, 9223372036854775807i64);
        assert_eq!(fmt!("%lli", llong_max), "9223372036854775807");
    }

    #[test]
    fn formats_unsigned_long_long_int_x86_64() {
        assert_eq!(fmt!("%llu", 42u64), "42");

        assert_eq!(fmt!("%llu", 0u64), "0");

        let ullong_max = std::u64::MAX;
        assert_eq!(ullong_max, 18446744073709551615u64);
        assert_eq!(fmt!("%llu", ullong_max), "18446744073709551615");
    }<commit_msg>Tidy up numeric tests for fmt!<commit_after>pub mod specifiers_iterator;

/// A safe-ish wrapper around `libc::snprintf`.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                // Might fail if `local_format` contains a null byte. TODO decide if this is a good
                // error-handling strategy, or we should report back to user, or panic, or what
                if let Ok(local_format_cstring) = std::ffi::CString::new(local_format) {
                    let buf_size = 1024usize;
                    let buffer: std::vec::Vec<u8> = std::vec::Vec::with_capacity(buf_size);
                    if let Ok(buffer) = std::ffi::CString::new(buffer) {
                        unsafe {
                            let buffer_ptr = buffer.into_raw();
                            // TODO: check how many bytes were written
                            let bytes_written = libc::snprintf(
                                buffer_ptr,
                                buf_size as libc::size_t,
                                local_format_cstring.as_ptr() as *const libc::c_char,
                                $arg);
                            let buffer = std::ffi::CString::from_raw(buffer_ptr);
                            let _bytes_written = bytes_written as usize;
                            if let Ok(formatted) = buffer.into_string() {
                                result.push_str(&formatted);
                            }
                        }
                    }
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!("%i", 42i32), "42");
        assert_eq!(fmt!("%i", std::i32::MIN), "-2147483648");
        assert_eq!(fmt!("%i", std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!("%u", 42u32), "42");
        assert_eq!(fmt!("%u", 0u32), "0");
        assert_eq!(fmt!("%u", std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!("%li", 42i64), "42");
        assert_eq!(fmt!("%li", std::i32::MIN as i64 - 1), "-2147483649");
        assert_eq!(fmt!("%li", std::i32::MAX as i64 + 1), "2147483648");

        assert_eq!(fmt!("%lli", 42i64), "42");
        assert_eq!(fmt!("%lli", std::i64::MIN), "-9223372036854775808");
        assert_eq!(fmt!("%lli", std::i64::MAX), "9223372036854775807");
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!("%lu", 42u64), "42");
        assert_eq!(fmt!("%lu", 0u64), "0");
        assert_eq!(fmt!("%lu", std::u64::MAX), "18446744073709551615");

        assert_eq!(fmt!("%llu", 42u64), "42");
        assert_eq!(fmt!("%llu", 0u64), "0");
        assert_eq!(fmt!("%llu", std::u64::MAX), "18446744073709551615");
    }
{'fraction_non_alphanumeric': 0.1571948170038645, 'fraction_numerical': 0.07558536030916117, 'mean_word_length': 1.931045969353764, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7188870', 'n_tokens_mistral': 3271, 'n_tokens_neox': 2819, 'n_words': 546}