#[cfg(target_env = "gnu")]
use crate::log_d;
#[cfg(target_env = "gnu")]
extern "C" {
fn malloc_trim(pad: libc::size_t) -> libc::c_int;
}
#[cfg(target_env = "gnu")]
pub fn try_release_unused_heap_memory() {
unsafe {
let result = malloc_trim(0);
if result == 0 {
log_d!("MemoryUtils", "No memory was released by malloc_trim.");
} else {
log_d!("MemoryUtils", "Memory was released by malloc_trim.");
}
}
}
#[cfg(not(target_env = "gnu"))]
pub fn try_release_unused_heap_memory() {
}
const LOGGABLE_KEY_PREFIX_LENGTH: usize = 13;
pub(crate) fn get_loggable_sdk_key(sdk_key: &str) -> String {
sdk_key.chars().take(LOGGABLE_KEY_PREFIX_LENGTH).collect()
}
pub(crate) fn split_host_and_path(url: &str) -> (String, &str) {
if let Some((scheme, after_scheme)) = url.split_once("://") {
let (host, path) = after_scheme.split_once('/').unwrap_or((after_scheme, ""));
return (format!("{scheme}://{host}/"), path);
}
(String::new(), url)
}
pub(crate) fn strip_query_and_fragment(path: &str) -> &str {
let no_query = path.split_once('?').map(|(path, _)| path).unwrap_or(path);
no_query
.split_once('#')
.map(|(path, _)| path)
.unwrap_or(no_query)
}
#[cfg(any(test, not(feature = "custom_network_provider")))]
pub(crate) fn url_path_has_suffix(url: &str, expected_suffix: &[&str]) -> bool {
let (_, raw_path) = split_host_and_path(url);
let segments: Vec<&str> = strip_query_and_fragment(raw_path)
.split('/')
.filter(|segment| !segment.is_empty())
.collect();
segments.ends_with(expected_suffix)
}
pub(crate) fn is_version_segment(segment: &str) -> bool {
segment.len() > 1
&& segment.starts_with('v')
&& segment[1..]
.chars()
.all(|character| character.is_ascii_digit())
}
pub fn get_api_from_url(url: &str) -> String {
let (base, path) = match url.find("://") {
Some(scheme_end) => {
let after_scheme = &url[scheme_end + 3..];
match after_scheme.find('/') {
Some(slash) => (
&url[..scheme_end + 3 + slash],
&url[scheme_end + 3 + slash..],
),
None => (url, ""),
}
}
None => (url, ""), };
if let Some(rest) = path.strip_prefix("/v") {
let digits: String = rest.chars().take_while(|c| c.is_ascii_digit()).collect();
if !digits.is_empty() {
let after = &rest[digits.len()..];
if after.is_empty() || after.starts_with('/') {
return format!("{base}/v{digits}");
}
}
}
base.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_split_host_and_path() {
assert_eq!(
split_host_and_path("https://api.example.com/v1/specs"),
("https://api.example.com/".to_string(), "v1/specs")
);
assert_eq!(
split_host_and_path("https://api.example.com"),
("https://api.example.com/".to_string(), "")
);
assert_eq!(
split_host_and_path("/v1/specs"),
(String::new(), "/v1/specs")
);
}
#[test]
fn test_strip_query_and_fragment() {
assert_eq!(strip_query_and_fragment("v1/specs?x=1"), "v1/specs");
assert_eq!(strip_query_and_fragment("v1/specs#frag"), "v1/specs");
assert_eq!(strip_query_and_fragment("v1/specs?x=1#frag"), "v1/specs");
assert_eq!(strip_query_and_fragment("v1/specs"), "v1/specs");
}
#[cfg(any(test, not(feature = "custom_network_provider")))]
#[test]
fn test_url_path_has_suffix() {
assert!(url_path_has_suffix(
"https://api.example.com/v1/log_event",
&["v1", "log_event"]
));
assert!(url_path_has_suffix(
"https://api.example.com/prefix/v1/log_event?foo=bar",
&["v1", "log_event"]
));
assert!(url_path_has_suffix(
"https://api.example.com/v1/log_event/",
&["v1", "log_event"]
));
assert!(!url_path_has_suffix(
"https://api.example.com/v1/log_event/extra",
&["v1", "log_event"]
));
assert!(!url_path_has_suffix(
"https://api.example.com/v1/log_events",
&["v1", "log_event"]
));
}
#[test]
fn test_is_version_segment() {
assert!(is_version_segment("v1"));
assert!(is_version_segment("v10"));
assert!(!is_version_segment("v"));
assert!(!is_version_segment("1"));
assert!(!is_version_segment("v1beta"));
assert!(!is_version_segment("V1"));
}
#[test]
fn test_get_network_api() {
let api = get_api_from_url("http://localhost:8080/v1/endpoint");
assert_eq!(api, "http://localhost:8080/v1".to_string());
let api = get_api_from_url("http://localhost:8080/v2/download_config_specs");
assert_eq!(api, "http://localhost:8080/v2".to_string());
let api = get_api_from_url("http://localhost:8080");
assert_eq!(api, "http://localhost:8080".to_string());
let api = get_api_from_url("https://api.example.com/v1/specs");
assert_eq!(api, "https://api.example.com/v1".to_string());
let api = get_api_from_url("https://api.statsig.com/v1/get_id_lists");
assert_eq!(api, "https://api.statsig.com/v1".to_string());
let api = get_api_from_url("https://api.statsigcdn.com/v1/download_id_list_file");
assert_eq!(api, "https://api.statsigcdn.com/v1".to_string());
let api = get_api_from_url("https://api.statsig.com/get_id_lists");
assert_eq!(api, "https://api.statsig.com".to_string());
let api = get_api_from_url("https://api.example.com/v10/specs");
assert_eq!(api, "https://api.example.com/v10".to_string());
let api = get_api_from_url("https://api.example.com/v1beta/specs");
assert_eq!(api, "https://api.example.com".to_string());
let api = get_api_from_url("https://api.example.com/v1/specs?x=1");
assert_eq!(api, "https://api.example.com/v1".to_string());
let api = get_api_from_url("https://api.example.com/v1/specs#frag");
assert_eq!(api, "https://api.example.com/v1".to_string());
let api = get_api_from_url("https://api.example.com/v1/foo/v2/bar");
assert_eq!(api, "https://api.example.com/v1".to_string());
let api = get_api_from_url("http://[::1]:8080/v1/specs");
assert_eq!(api, "http://[::1]:8080/v1".to_string());
let api = get_api_from_url("https://api.example.com/v1/");
assert_eq!(api, "https://api.example.com/v1".to_string());
let api = get_api_from_url("");
assert_eq!(api, "".to_string());
}
}