#![no_std]
#[doc(hidden)]
pub const BUFFER_SIZE: usize = match option_env!("VER_STUB_BUFFER_SIZE") {
Some(s) => match u16::from_str_radix(s, 10) {
Ok(n) => n as usize,
Err(_) => panic!("VER_STUB_BUFFER_SIZE must be a valid u16 integer (0-65535)"),
},
None => 512,
};
#[doc(hidden)]
pub const fn header_size(num_members: usize) -> usize {
1 + num_members * 2
}
const _: () = assert!(
header_size(Member::COUNT) <= 32,
"header_size(Member::COUNT) exceeds 32, these asserts must be updated"
);
const _: () = assert!(
BUFFER_SIZE > 32,
"VER_STUB_BUFFER_SIZE must be greater than 32"
);
#[doc(hidden)]
pub const SECTION_NAME: &str = ".ver_stub";
#[doc(hidden)]
#[repr(u16)]
#[derive(Clone, Copy)]
pub enum Member {
GitSha = 0,
GitDescribe = 1,
GitBranch = 2,
GitCommitTimestamp = 3,
GitCommitDate = 4,
GitCommitMsg = 5,
BuildTimestamp = 6,
BuildDate = 7,
Custom = 8,
}
impl Member {
#[doc(hidden)]
pub const COUNT: usize = 9;
}
#[unsafe(link_section = ".ver_stub")]
#[used]
static BUFFER: [u8; BUFFER_SIZE] = [0u8; BUFFER_SIZE];
#[inline(never)]
fn read_buffer_byte(index: usize) -> u8 {
unsafe { core::ptr::read_volatile(BUFFER.as_ptr().add(index)) }
}
fn read_buffer_u16(offset: usize) -> u16 {
let lo = read_buffer_byte(offset) as u16;
let hi = read_buffer_byte(offset + 1) as u16;
lo | (hi << 8)
}
fn get_member(member: Member) -> Option<&'static str> {
let idx = member as usize;
let actual_num_members = read_buffer_byte(0) as usize;
if actual_num_members == 0 {
return None;
}
if idx >= actual_num_members {
return None;
}
let actual_header_size = header_size(actual_num_members);
let end_offset_pos = 1 + idx * 2;
let end = actual_header_size + read_buffer_u16(end_offset_pos) as usize;
let start = if idx == 0 {
actual_header_size
} else {
let prev_end_pos = 1 + (idx - 1) * 2;
actual_header_size + read_buffer_u16(prev_end_pos) as usize
};
if start == end {
return None;
}
if end < start {
panic!(
"ver-stub: invalid range for {:?}: start={}, end={}",
member as u16, start, end
);
}
if end > BUFFER_SIZE {
panic!(
"ver-stub: end offset {} exceeds buffer size {} for {:?}",
end, BUFFER_SIZE, member as u16
);
}
let bytes = core::hint::black_box(&BUFFER[start..end]);
match core::str::from_utf8(bytes) {
Ok(s) => Some(s),
Err(e) => panic!("ver-stub: invalid UTF-8 for {:?}: {:?}", member as u16, e),
}
}
pub fn git_sha() -> Option<&'static str> {
get_member(Member::GitSha)
}
pub fn git_describe() -> Option<&'static str> {
get_member(Member::GitDescribe)
}
pub fn git_branch() -> Option<&'static str> {
get_member(Member::GitBranch)
}
pub fn git_commit_timestamp() -> Option<&'static str> {
get_member(Member::GitCommitTimestamp)
}
pub fn git_commit_date() -> Option<&'static str> {
get_member(Member::GitCommitDate)
}
pub fn git_commit_msg() -> Option<&'static str> {
get_member(Member::GitCommitMsg)
}
pub fn build_timestamp() -> Option<&'static str> {
get_member(Member::BuildTimestamp)
}
pub fn build_date() -> Option<&'static str> {
get_member(Member::BuildDate)
}
pub fn custom() -> Option<&'static str> {
get_member(Member::Custom)
}