d-major 0.0.0

Traverse directory trees in parallel, using relative entries to minimize allocation and maximize parallelism.
Documentation
/*
 * Description: Methods to interop with null-terminated C strings.
 * This code is largely copied from the rust compiler's small_c_string.rs: https://github.com/rust-lang/rust/blob/52daa7d835e7ff51cb387340082bf9a59b949738/library/std/src/sys/pal/common/small_c_string.rs.
 * The rust compiler is distributed under both the MIT and Apache v2 licenses.
 *
 * Copyright (C) 2025 d@nny mc² <dmc2@hypnicjerk.ai>
 * SPDX-License-Identifier: LGPL-3.0-or-later
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

//! Methods to interop with null-terminated C strings.
//!
//! This code is largely copied from the rust compiler's [`small_c_string.rs`](https://github.com/rust-lang/rust/blob/52daa7d835e7ff51cb387340082bf9a59b949738/library/std/src/sys/pal/common/small_c_string.rs).

use std::{
  ffi::{CStr, CString},
  io,
  mem::MaybeUninit,
  path::Path,
  ptr, slice,
};


// Make sure to stay under 4096 so the compiler doesn't insert a probe frame:
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
cfg_if::cfg_if! {
  if #[cfg(target_os = "espidf")] {
    pub const MAX_STACK_ALLOCATION: usize = 32;
  } else {
    pub const MAX_STACK_ALLOCATION: usize = 384;
  }
}


cfg_if::cfg_if! {
  if #[cfg(feature = "nightly")] {
    const fn null_err() -> io::Error {
      io::const_error!(
        io::ErrorKind::InvalidInput,
        "file name contained an unexpected NUL byte"
      )
    }
  } else {
    fn null_err() -> io::Error {
      io::Error::new(
          io::ErrorKind::InvalidInput,
          "file name contained an unexpected NUL byte"
        )
    }
  }
}


// rustc dyn erases the closure type to avoid bloat in codegen
// (https://github.com/rust-lang/rust/pull/121101), but we're not doing the whole stdlib and care
// less about code bloat for the small number of call sites in this library.
#[inline]
pub fn run_path_with_cstr<T>(path: &Path, f: impl FnOnce(&CStr) -> io::Result<T>) -> io::Result<T> {
  run_with_cstr(path.as_os_str().as_encoded_bytes(), f)
}


#[inline]
pub fn run_with_cstr<T>(bytes: &[u8], f: impl FnOnce(&CStr) -> io::Result<T>) -> io::Result<T> {
  if bytes.len() >= MAX_STACK_ALLOCATION {
    run_with_cstr_allocating(bytes, f)
  } else {
    unsafe { run_with_cstr_stack(bytes, f) }
  }
}

/// # Safety
///
/// `bytes` must have a length less than [`MAX_STACK_ALLOCATION`].
unsafe fn run_with_cstr_stack<T>(
  bytes: &[u8],
  f: impl FnOnce(&CStr) -> io::Result<T>,
) -> io::Result<T> {
  let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
  let buf_ptr = buf.as_mut_ptr() as *mut u8;

  unsafe {
    ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
    buf_ptr.add(bytes.len()).write(0);
  }

  match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
    Ok(s) => f(s),
    Err(_) => Err(null_err()),
  }
}


#[cold]
#[inline(never)]
fn run_with_cstr_allocating<T>(
  bytes: &[u8],
  f: impl FnOnce(&CStr) -> io::Result<T>,
) -> io::Result<T> {
  match CString::new(bytes) {
    Ok(s) => f(&s),
    Err(_) => Err(null_err()),
  }
}