### DirEntries iterator example usage Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Example from the documentation comments showing how to use the read_dir function and iterate over directory entries. ```rust use getattrlistbulk::{read_dir, RequestedAttributes}; let entries = read_dir("/tmp", RequestedAttributes::all())?; for entry in entries { let entry = entry?; println!("{}", entry.name); } # Ok::<(), getattrlistbulk::Error>(()) ``` -------------------------------- ### DirReader builder example Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Shows the fluent builder API for reading directory entries. Requests name, size, and object type with a custom buffer size, then iterates over results. The `read()` method returns a `Result` that must be handled with `?`. ```rust use getattrlistbulk::DirReader; let entries = DirReader::new("/tmp") .name() .size() .object_type() .buffer_size(128 * 1024) .read()?; for entry in entries { let entry = entry?; println!("{:?} bytes", entry.size); } # Ok::<(), getattrlistbulk::Error>(()) ``` -------------------------------- ### Basic directory enumeration with read_dir Source: https://docs.rs/getattrlistbulk Shows how to use the crate to enumerate a directory and print each entry's name and size. The example requests the name and size attributes, iterates over the results, and prints the entry name and size (defaulting to 0 if size is unavailable). ```rust use getattrlistbulk::{read_dir, RequestedAttributes}; let attrs = RequestedAttributes { name: true, size: true, ..Default::default() }; for entry in read_dir("/Users", attrs).unwrap() { let entry = entry.unwrap(); println!("{}: {} bytes", entry.name, entry.size.unwrap_or(0)); } ``` -------------------------------- ### Using read_dir with DirEntries Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirEntries.html Example of iterating over directory entries using read_dir and RequestedAttributes::all() for the /tmp directory. ```rust use getattrlistbulk::{read_dir, RequestedAttributes}; let entries = read_dir("/tmp", RequestedAttributes::all())?; for entry in entries { let entry = entry?; println!("{}", entry.name); } ``` -------------------------------- ### read_dir example Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/fn.read_dir.html Example usage of read_dir with a RequestedAttributes struct. It reads entries from /tmp and prints each entry. The ? operator propagates errors. ```rust use getattrlistbulk::{read_dir, RequestedAttributes}; let attrs = RequestedAttributes { name: true, size: true, object_type: true, ..Default::default() }; for entry in read_dir("/tmp", attrs)? { println!("{:?}", entry?); } ``` -------------------------------- ### DirReader usage example Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Example usage of DirReader to read directory entries with name, size, and object type attributes, using a custom buffer size of 128KB. The read() method returns an iterator over entries, and each entry is unwrapped with ?. ```rust use getattrlistbulk::DirReader; let entries = DirReader::new("/tmp") .name() .size() .object_type() .buffer_size(128 * 1024) .read()?; for entry in entries { let entry = entry?; println!("{}: {:?} bytes", entry.name, entry.size); } ``` -------------------------------- ### Read directory entries with read_dir and RequestedAttributes Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/lib.rs.html Shows how to use the `read_dir` function with a `RequestedAttributes` struct to enumerate directory entries, printing each entry's name and size. The example uses `unwrap()` on the iterator and each entry, and is marked `no_run` in the source. ```rust use getattrlistbulk::{read_dir, RequestedAttributes}; let attrs = RequestedAttributes { name: true, size: true, ..Default::default() }; for entry in read_dir("/Users", attrs).unwrap() { let entry = entry.unwrap(); println!("{:?}", entry); } ``` -------------------------------- ### Request only name and size attributes Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.RequestedAttributes.html Shows how to create a RequestedAttributes instance requesting only the name and size attributes, using Default::default() for the remaining fields. This is the documented example for using the struct. ```rust use getattrlistbulk::RequestedAttributes; // Request only name and size let attrs = RequestedAttributes { name: true, size: true, ..Default::default() }; ``` -------------------------------- ### Syscall complexity comparison Source: https://docs.rs/getattrlistbulk Compares the syscall complexity of traditional directory enumeration with this crate's approach. The traditional method uses one readdir and stat per file, while this crate uses a single getattrlistbulk call per batch of up to 800 entries. ```text Traditional: opendir() → [readdir() + stat()] × N → closedir() This crate: open() → getattrlistbulk() × ceil(N/800) → close() ``` -------------------------------- ### open_directory helper function Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Opens a directory path and returns a raw file descriptor. Converts the path to a C string, calls libc::open with O_RDONLY and O_DIRECTORY flags, and returns an error if the path contains a null byte or if open fails. ```rust fn open_directory(path: &Path) -> Result { use std::ffi::CString; use std::os::unix::ffi::OsStrExt; let c_path = CString::new(path.as_os_str().as_bytes()) .map_err(|_| Error::Open(std::io::Error::new( std::io::ErrorKind::InvalidInput, "path contains null byte", )))?; let fd = unsafe { libc::open(c_path.as_ptr(), libc::O_RDONLY | libc::O_DIRECTORY) }; if fd < 0 { return Err(Error::Open(std::io::Error::last_os_error())); } Ok(fd) } ``` -------------------------------- ### Iterator implementation for DirEntries Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Implements the Iterator trait for DirEntries, yielding Result. The next method loops, first trying to get an entry from the current buffer, and if exhausted, refills the buffer. Returns None when exhausted. ```rust impl Iterator for DirEntries { type Item = Result; fn next(&mut self) -> Option { loop { // Try to get next entry from current buffer if let Some(result) = self.next_from_buffer() { return Some(result); } // Buffer exhausted, try to refill if self.exhausted { return None; } match self.refill_buffer() { Ok(true) => continue, Ok(false) => return None, Err(e) => return Some(Err(e)), } } } } ``` -------------------------------- ### Performance Comparison of getattrlistbulk vs Traditional Directory Enumeration Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk Shows the system call patterns for traditional readdir+stat vs getattrlistbulk, highlighting the reduction in calls per directory. ```text Traditional: opendir() → [readdir() + stat()] × ``` -------------------------------- ### RequestedAttributes::all() Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.RequestedAttributes.html Creates a RequestedAttributes instance with all attributes requested. ```APIDOC ## RequestedAttributes::all() ### Description Creates a RequestedAttributes instance with all attributes requested. ### Method `pub fn all() -> Self` ### Parameters None. ### Returns - `Self` - A RequestedAttributes with all fields set to `true`. ### Example ```rust use getattrlistbulk::RequestedAttributes; let attrs = RequestedAttributes::all(); ``` ``` -------------------------------- ### DirEntries::new method Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Creates a new directory iterator by opening the directory and initializing the buffer. ```rust pub(crate) fn new( path: &Path, requested: RequestedAttributes, buffer_size: usize, follow_symlinks: bool, ) -> Result { let dirfd = open_directory(path)?; Ok(Self { dirfd, buffer: vec![0u8; buffer_size], bytes_valid: 0, parser_offset: 0, requested, exhausted: false, follow_symlinks, }) } ``` -------------------------------- ### Verify all_attributes() enables all attribute flags Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Creates a DirReader for /tmp with all attributes enabled and asserts that each attribute flag (name, size, object_type, permissions, inode) is set. Use to verify that all_attributes() enables every available attribute. ```rust let reader = DirReader::new("/tmp").all_attributes(); assert!(reader.attrs.name); assert!(reader.attrs.size); assert!(reader.attrs.object_type); assert!(reader.attrs.permissions); assert!(reader.attrs.inode); ``` -------------------------------- ### RequestedAttributes builder methods (all, with_name, with_object_type, with_size, with_alloc_size, with_modified_time, with_permissions, with_inode, with_entry_count) Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/types.rs.html Provides builder methods to construct RequestedAttributes. The all() method requests all attributes, and each with_* method enables a specific attribute. ```rust impl RequestedAttributes { /// Request all available attributes. pub fn all() -> Self { Self { name: true, object_type: true, size: true, alloc_size: true, modified_time: true, permissions: true, inode: true, entry_count: true, } } /// Builder method to request name. pub fn with_name(mut self) -> Self { self.name = true; self } /// Builder method to request object type. pub fn with_object_type(mut self) -> Self { self.object_type = true; self } /// Builder method to request size. pub fn with_size(mut self) -> Self { self.size = true; self } /// Builder method to request allocation size. pub fn with_alloc_size(mut self) -> Self { self.alloc_size = true; self } /// Builder method to request modification time. pub fn with_modified_time(mut self) -> Self { self.modified_time = true; self } /// Builder method to request permissions. pub fn with_permissions(mut self) -> Self { self.permissions = true; self } /// Builder method to request inode. pub fn with_inode(mut self) -> Self { self.inode = true; self } /// Builder method to request entry count. pub fn with_entry_count(mut self) -> Self { self.entry_count = true; self } } ``` -------------------------------- ### DirReader::new Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Creates a new directory reader for the given path with default settings. ```APIDOC ## DirReader::new ### Description Creates a new directory reader for the given path with default settings. ### Method new ### Signature `pub fn new>(path: P) -> Self` ### Parameters - **path** (P) - Required - The path to the directory to read. ### Returns - **Self** - A new `DirReader` instance with default buffer size (64KB) and follow_symlinks set to true. ### Example rust let reader = DirReader::new("/tmp"); ``` -------------------------------- ### Buffer format diagram for getattrlistbulk entries Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/parser.rs.html ASCII diagram showing the layout of each entry in the getattrlistbulk result buffer, including the length field, attribute set, fixed attributes, attribute reference, and variable data. ```text +------------------+ | length (u32) | Total length of this entry +------------------+ | attribute_set | Which attributes are present (20 bytes) +------------------+ | fixed attrs | Fixed-size attributes in order +------------------+ | attrreference | For names: offset (i32) + length (u32) +------------------+ | variable data | Variable-length data at end +------------------+ ``` -------------------------------- ### read_dir function with default buffer size Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/lib.rs.html The `read_dir` function signature and implementation, which delegates to `read_dir_with_buffer` with a default buffer size of 64KB. It returns a `Result` and is the primary entry point for directory enumeration. ```rust pub fn read_dir>( path: P, attrs: RequestedAttributes, ) -> Result { read_dir_with_buffer(path, attrs, 64 * 1024) } ``` -------------------------------- ### DirReader::all_attributes Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests all available attributes. ```APIDOC ## DirReader::all_attributes ### Description Requests all available attributes. ### Method all_attributes ### Returns - **Self** - The `DirReader` instance with all attributes requested. ``` -------------------------------- ### DirEntries::next_from_buffer method Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Parses the next entry from the current buffer position using BufferParser. ```rust fn next_from_buffer(&mut self) -> Option> { if self.parser_offset >= self.bytes_valid { return None; } let mut parser = BufferParser::new( &self.buffer[self.parser_offset..], self.bytes_valid - self.parser_offset, self.requested, ); match parser.next_entry() { Some(Ok(entry)) => { ``` -------------------------------- ### read_dir_with_buffer function with custom buffer size Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/lib.rs.html The `read_dir_with_buffer` function signature and implementation, which allows specifying a custom buffer size. Larger buffers reduce syscalls but use more memory; recommended sizes are 64KB (default), 256KB for large directories, and 1MB for very large directories. ```rust pub fn read_dir_with_buffer>( path: P, attrs: RequestedAttributes, buffer_size: usize, ) -> Result { DirEntries::new(path.as_ref(), attrs, buffer_size, true) } ``` -------------------------------- ### Parse directory entry attributes with BufferParser methods Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/parser.rs.html These methods extract directory attributes from the buffer, reading values such as entry counts, and convert timespec values to SystemTime. They are part of the BufferParser's internal implementation. ```rust offset += 8; } // Directory attributes if returned.dirattr & ffi::DirAttr::ENTRYCOUNT.bits() != 0 { entry_count = Some(self.read_u32(offset)?); // offset += 4; // Not needed, we're done } Ok(DirEntry { name, object_type, size, alloc_size, modified_time, permissions, inode, entry_count, }) } ``` ```rust fn read_u32(&self, offset: usize) -> Result { if offset + 4 > self.buffer.len() { return Err(ParseError::UnexpectedEnd); } let bytes: [u8; 4] = self.buffer[offset..offset + 4] .try_into() .map_err(|_| ParseError::UnexpectedEnd)?; Ok(u32::from_ne_bytes(bytes)) } ``` ```rust fn read_u64(&self, offset: usize) -> Result { if offset + 8 > self.buffer.len() { return Err(ParseError::UnexpectedEnd); } let bytes: [u8; 8] = self.buffer[offset..offset + 8] .try_into() .map_err(|_| ParseError::UnexpectedEnd)?; Ok(u64::from_ne_bytes(bytes)) } ``` ```rust fn read_i32(&self, offset: usize) -> Result { if offset + 4 > self.buffer.len() { return Err(ParseError::UnexpectedEnd); } let bytes: [u8; 4] = self.buffer[offset..offset + 4] .try_into() .map_err(|_| ParseError::UnexpectedEnd)?; Ok(i32::from_ne_bytes(bytes)) } ``` -------------------------------- ### DirReader::new Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Creates a new directory reader for the given path. ```APIDOC ## DirReader::new ### Description Creates a new directory reader for the given path. ### Method new ### Parameters - **path** (AsRef) - Required - The path to the directory to read. ### Returns - **Self** - A new `DirReader` instance. ``` -------------------------------- ### DirReader::alloc_size Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests allocated sizes on disk. ```APIDOC ## DirReader::alloc_size ### Description Requests allocated sizes on disk. ### Method alloc_size ### Returns - **Self** - The `DirReader` instance with the allocated size attribute requested. ``` -------------------------------- ### BufferParser implementation for getattrlistbulk Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/parser.rs.html Rust implementation of the BufferParser struct, which parses the raw byte buffer from getattrlistbulk into DirEntry values. Includes methods for creating a parser, parsing the next entry, resetting the parser, and internal helpers for reading attributes. ```rust use crate::error::ParseError; use crate::ffi; use crate::types::{DirEntry, ObjectType, RequestedAttributes}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; /// Parser for getattrlistbulk result buffer. pub(crate) struct BufferParser<'a> { buffer: &'a [u8], offset: usize, bytes_valid: usize, _requested: RequestedAttributes, } impl<'a> BufferParser<'a> { /// Create a new parser for the given buffer. pub fn new(buffer: &'a [u8], bytes_valid: usize, requested: RequestedAttributes) -> Self { Self { buffer, offset: 0, bytes_valid, _requested: requested, } } /// Parse the next entry from the buffer. /// /// Returns `None` when the buffer is exhausted. /// Returns `Some(Err(...))` on parse errors. pub fn next_entry(&mut self) -> Option> { if self.offset >= self.bytes_valid { return None; } // Read entry length let entry_length = match self.read_u32(self.offset) { Ok(len) => len as usize, Err(e) => return Some(Err(e)), }; if entry_length == 0 { return Some(Err(ParseError::InvalidEntryLength)); } if self.offset + entry_length > self.bytes_valid { return Some(Err(ParseError::BufferTooSmall)); } let result = self.parse_entry(self.offset, entry_length); self.offset += entry_length; Some(result) } /// Reset parser for a new buffer. #[allow(dead_code)] pub fn reset(&mut self, buffer: &'a [u8], bytes_valid: usize) { self.buffer = buffer; self.bytes_valid = bytes_valid; self.offset = 0; } fn parse_entry(&self, entry_start: usize, _entry_length: usize) -> Result { let mut offset = entry_start + 4; // Skip length field // Read returned attributes bitmap let returned = self.read_attribute_set(offset)?; offset += std::mem::size_of::(); // Parse attributes in order based on what was returned let mut name = String::new(); let mut object_type = None; let mut size = None; let mut alloc_size = None; let mut modified_time = None; let mut permissions = None; let mut inode = None; let mut entry_count = None; // Common attributes (in order defined by macOS) if returned.commonattr & ffi::CommonAttr::NAME.bits() != 0 { let (parsed_name, new_offset) = self.parse_attrreference_string(entry_start, offset)?; name = parsed_name; offset = new_offset; } if returned.commonattr & ffi::CommonAttr::OBJTYPE.bits() != 0 { let vtype = self.read_u32(offset)?; object_type = Some(ObjectType::from(vtype)); offset += 4; } if returned.commonattr & ffi::CommonAttr::MODTIME.bits() != 0 { let (time, new_offset) = self.parse_timespec(offset)?; modified_time = Some(time); offset = new_offset; } if returned.commonattr & ffi::CommonAttr::ACCESSMASK.bits() != 0 { permissions = Some(self.read_u32(offset)?); offset += 4; } if returned.commonattr & ffi::CommonAttr::FILEID.bits() != 0 { inode = Some(self.read_u64(offset)?); offset += 8; } // File attributes if returned.fileattr & ffi::FileAttr::TOTALSIZE.bits() != 0 { size = Some(self.read_u64(offset)?); offset += 8; } if returned.fileattr & ffi::FileAttr::ALLOCSIZE.bits() != 0 { alloc_size = Some(self.read_u64(offset)?); } } } ``` -------------------------------- ### DirReader::follow_symlinks Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Controls whether symbolic links are followed during the directory read. ```APIDOC ## DirReader::follow_symlinks ### Description Controls whether symbolic links are followed during the directory read. When `false`, uses FSOPT_NOFOLLOW to not follow symlinks. Default is `true` (follow symlinks). ### Method follow_symlinks ### Signature `pub fn follow_symlinks(mut self, follow: bool) -> Self` ### Parameters - **follow** (bool) - Required - Whether to follow symbolic links. ### Returns - **Self** - The builder instance with the follow_symlinks setting. ### Example rust let reader = DirReader::new("/tmp").follow_symlinks(false); ``` -------------------------------- ### DirReader::all_attributes Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests all available attributes in the directory read. ```APIDOC ## DirReader::all_attributes ### Description Requests all available attributes in the directory read. ### Method all_attributes ### Signature `pub fn all_attributes(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with all attributes requested. ### Example rust let reader = DirReader::new("/tmp").all_attributes(); ``` -------------------------------- ### Unit tests for read_u32 bounds and value Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/parser.rs.html Two unit tests verify read_u32 returns the correct value and returns an error when the buffer is too short. The tests use a BufferParser instance with a RequestedAttributes::default() configuration. ```rust #[test] fn test_read_u32() { let buffer = [0x01, 0x02, 0x03, 0x04]; let parser = BufferParser::new(&buffer, buffer.len(), RequestedAttributes::default()); let value = parser.read_u32(0).unwrap(); assert_eq!(value, u32::from_ne_bytes([0x01, 0x02, 0x03, 0x04])); } ``` ```rust #[test] fn test_read_u32_bounds() { let buffer = [0x01, 0x02]; let parser = BufferParser::new(&buffer, buffer.len(), RequestedAttributes::default()); ``` -------------------------------- ### Conversion from RequestedAttributes to ffi::attrlist Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/types.rs.html Implements From for ffi::attrlist, mapping each requested attribute to the corresponding FFI bit flags. Sets bitmapcount to ffi::ATTR_BIT_MAP_COUNT and reserved to 0. ```rust impl From for ffi::attrlist { fn from(req: RequestedAttributes) -> Self { let mut common = ffi::CommonAttr::RETURNED_ATTRS; let mut file = ffi::FileAttr::empty(); let mut dir = ffi::DirAttr::empty(); if req.name { common |= ffi::CommonAttr::NAME; } if req.object_type { common |= ffi::CommonAttr::OBJTYPE; } if req.modified_time { common |= ffi::CommonAttr::MODTIME; } if req.permissions { common |= ffi::CommonAttr::ACCESSMASK; } if req.inode { common |= ffi::CommonAttr::FILEID; } if req.size { file |= ffi::FileAttr::TOTALSIZE; } if req.alloc_size { file |= ffi::FileAttr::ALLOCSIZE; } if req.entry_count { dir |= ffi::DirAttr::ENTRYCOUNT; } ffi::attrlist { bitmapcount: ffi::ATTR_BIT_MAP_COUNT, reserved: 0, commonattr: common.bits(), volattr: 0, dirattr: dir.bits(), fileattr: file.bits(), forkattr: 0, } } } ``` -------------------------------- ### DirReader::permissions Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests Unix permissions. ```APIDOC ## DirReader::permissions ### Description Requests Unix permissions. ### Method permissions ### Returns - **Self** - The `DirReader` instance with the permissions attribute requested. ``` -------------------------------- ### DirEntry Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirEntry.html Struct representing metadata for a single directory entry. ```APIDOC ## Struct DirEntry ### Description Metadata for a single directory entry. ### Fields - **name** (String) - File or directory name - **object_type** (Option) - Object type - **size** (Option) - Total size in bytes - **alloc_size** (Option) - Allocated size on disk - **modified_time** (Option) - Last modification time - **permissions** (Option) - Unix permissions mask - **inode** (Option) - Inode number / file ID - **entry_count** (Option) - Entry count (directories only) ### Methods #### is_dir ```rust pub fn is_dir(&self) -> bool ``` Check if this entry is a directory. #### is_file ```rust pub fn is_file(&self) -> bool ``` Check if this entry is a regular file. #### is_symlink ```rust pub fn is_symlink(&self) -> bool ``` Check if this entry is a symbolic link. ### String Handling The `name` field uses lossy UTF-8 conversion. If a filename contains invalid UTF-8 sequences (rare on macOS but possible), invalid bytes are replaced with the Unicode replacement character (U+FFFD). ``` -------------------------------- ### DirReader::name Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests file/directory names. ```APIDOC ## DirReader::name ### Description Requests file/directory names. ### Method name ### Returns - **Self** - The `DirReader` instance with the name attribute requested. ``` -------------------------------- ### DirReader::permissions Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests Unix permissions in the directory read. ```APIDOC ## DirReader::permissions ### Description Requests Unix permissions in the directory read. ### Method permissions ### Signature `pub fn permissions(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with the permissions attribute requested. ### Example rust let reader = DirReader::new("/tmp").permissions(); ``` -------------------------------- ### DirReader::alloc_size Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests allocated sizes on disk in the directory read. ```APIDOC ## DirReader::alloc_size ### Description Requests allocated sizes on disk in the directory read. ### Method alloc_size ### Signature `pub fn alloc_size(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with the alloc_size attribute requested. ### Example rust let reader = DirReader::new("/tmp").alloc_size(); ``` -------------------------------- ### DirReader::size Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests file sizes in the directory read. ```APIDOC ## DirReader::size ### Description Requests file sizes in the directory read. ### Method size ### Signature `pub fn size(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with the size attribute requested. ### Example rust let reader = DirReader::new("/tmp").size(); ``` -------------------------------- ### DirReader::size Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests file sizes. ```APIDOC ## DirReader::size ### Description Requests file sizes. ### Method size ### Returns - **Self** - The `DirReader` instance with the size attribute requested. ``` -------------------------------- ### RequestedAttributes::with_alloc_size(self) Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.RequestedAttributes.html Builder method to request the allocation size attribute. ```APIDOC ## RequestedAttributes::with_alloc_size(self) ### Description Builder method to request the allocation size attribute. ### Method `pub fn with_alloc_size(self) -> Self` ### Parameters - `self` - The current RequestedAttributes instance. ### Returns - `Self` - A new RequestedAttributes with `alloc_size` set to `true`. ### Example ```rust use getattrlistbulk::RequestedAttributes; let attrs = RequestedAttributes::default().with_alloc_size(); ``` ``` -------------------------------- ### DirReader::follow_symlinks Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Controls whether symbolic links are followed. When `false`, uses FSOPT_NOFOLLOW to not follow symlinks. Default is `true` (follow symlinks). ```APIDOC ## DirReader::follow_symlinks ### Description Controls whether symbolic links are followed. When `false`, uses FSOPT_NOFOLLOW to not follow symlinks. Default is `true` (follow symlinks). ### Method follow_symlinks ### Parameters - **follow** (bool) - Required - Whether to follow symbolic links. ### Returns - **Self** - The `DirReader` instance with the symlink following behavior set. ``` -------------------------------- ### getattrlistbulk Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/ffi.rs.html FFI declaration for the macOS getattrlistbulk system call. This function performs bulk directory enumeration with attribute retrieval. It is unsafe and requires valid pointers and a valid directory file descriptor. ```APIDOC ## getattrlistbulk ### Description Bulk directory enumeration with attribute retrieval. This FFI function is unsafe and requires valid pointers and a valid directory file descriptor. ### Method FFI function call ### Signature ```c ssize_t getattrlistbulk( int dirfd, attrlist *alist, void *attribute_buffer, size_t buffer_size, uint64_t options ); ``` ### Parameters - **dirfd** (int) - Required - A valid open directory file descriptor. - **alist** (attrlist*) - Required - Pointer to a valid attrlist structure. - **attribute_buffer** (void*) - Required - Pointer to a buffer of at least `buffer_size` bytes. - **buffer_size** (size_t) - Required - Size of the attribute buffer. - **options** (uint64_t) - Required - Options for the call, see FsOptions bitflags. ### Safety - `dirfd` must be a valid open directory file descriptor. - `alist` must point to a valid attrlist structure. - `attribute_buffer` must point to a buffer of at least `buffer_size` bytes. ### Return Value Returns the number of bytes written to the attribute buffer, or -1 on error (errno is set). ``` -------------------------------- ### DirReader::entry_count Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests entry counts (for directories). ```APIDOC ## DirReader::entry_count ### Description Requests entry counts (for directories). ### Method entry_count ### Returns - **Self** - The `DirReader` instance with the entry count attribute requested. ``` -------------------------------- ### read_dir_with_buffer Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/fn.read_dir_with_buffer.html Reads directory entries with a custom buffer size. Larger buffers reduce syscalls but increase memory usage. ```APIDOC ## Function read_dir_with_buffer ### Description Reads directory entries with a custom buffer size. Larger buffers result in fewer syscalls but use more memory. ### Signature ```rust pub fn read_dir_with_buffer>( path: P, attrs: RequestedAttributes, buffer_size: usize, ) -> Result ``` ### Parameters - **path** (`P: AsRef`) - The directory path to read. - **attrs** (`RequestedAttributes`) - The attributes to request for each entry. - **buffer_size** (`usize`) - The size of the buffer in bytes. Recommended sizes: 64KB (default), 256KB for large directories (10,000+ files), 1MB for very large directories (100,000+ files). ### Return Value - **Result** - On success, returns a `DirEntries` iterator. On failure, returns an `Error`. ### Notes - Larger buffers reduce the number of syscalls but increase memory usage. - Recommended buffer sizes: 64KB (default), 256KB for large directories (10,000+ files), 1MB for very large directories (100,000+ files). ``` -------------------------------- ### RequestedAttributes::with_size(self) Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.RequestedAttributes.html Builder method to request the size attribute. ```APIDOC ## RequestedAttributes::with_size(self) ### Description Builder method to request the size attribute. ### Method `pub fn with_size(self) -> Self` ### Parameters - `self` - The current RequestedAttributes instance. ### Returns - `Self` - A new RequestedAttributes with `size` set to `true`. ### Example ```rust use getattrlistbulk::RequestedAttributes; let attrs = RequestedAttributes::default().with_size(); ``` ``` -------------------------------- ### DirEntries::refill_buffer method Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Refills the buffer with more entries using getattrlistbulk, handling EINTR and exhaustion. ```rust fn refill_buffer(&mut self) -> Result { let mut attrlist: ffi::attrlist = self.requested.into(); let mut options = ffi::FsOptions::PACK_INVAL_ATTRS; if !self.follow_symlinks { options |= ffi::FsOptions::NOFOLLOW; } let result = unsafe { ffi::getattrlistbulk( self.dirfd, &mut attrlist, self.buffer.as_mut_ptr() as *mut libc::c_void, self.buffer.len(), options.bits(), ) }; if result < 0 { let err = std::io::Error::last_os_error(); // Handle EINTR by retrying if err.raw_os_error() == Some(libc::EINTR) { return self.refill_buffer(); } return Err(Error::Syscall(err)); } if result == 0 { self.exhausted = true; return Ok(false); } // result contains the number of entries, but we need bytes // The buffer format tells us how many bytes via entry lengths // For now, we'll track bytes as we parse self.bytes_valid = self.find_valid_bytes(); self.parser_offset = 0; Ok(true) } ``` -------------------------------- ### DirReader::read Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Reads the directory and returns an iterator over entries. ```APIDOC ## DirReader::read ### Description Reads the directory and returns an iterator over entries. The `name` attribute is always requested regardless of configuration, as entries without names are not useful. If you explicitly set `name: false`, it will be overridden to `true`. ### Method read ### Signature `pub fn read(self) -> Result` ### Parameters None. ### Returns - **Result** - An iterator over directory entries, or an error if reading fails. ### Example rust let entries = DirReader::new("/tmp") .name() .size() .object_type() .buffer_size(128 * 1024) .read()?; for entry in entries { let entry = entry?; println!("{}: {:?} bytes", entry.name, entry.size); } ``` -------------------------------- ### read_dir Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/lib.rs.html Reads directory entries with specified attributes using a default buffer size of 64KB. Returns an iterator over directory entries. ```APIDOC ## read_dir ### Description Reads directory entries with specified attributes using a default buffer size of 64KB. Returns an iterator over directory entries. ### Method Function call ### Endpoint `read_dir>(path: P, attrs: RequestedAttributes) -> Result` ### Parameters - **path** (P) - Required - Path to the directory to read. - **attrs** (RequestedAttributes) - Required - Attributes to request for each entry. ### Request Example ```rust use getattrlistbulk::{read_dir, RequestedAttributes}; let attrs = RequestedAttributes { name: true, size: true, object_type: true, ..Default::default() }; for entry in read_dir("/tmp", attrs)? { println!("{:?}", entry?); } ``` ### Response #### Success Response Returns a `DirEntries` iterator that yields `Result` items. #### Response Example ```rust // Iterating over entries for entry in read_dir("/tmp", attrs)? { println!("{:?}", entry?); } ``` ``` -------------------------------- ### DirReader::name Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests file/directory names in the directory read. ```APIDOC ## DirReader::name ### Description Requests file/directory names in the directory read. ### Method name ### Signature `pub fn name(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with the name attribute requested. ### Example rust let reader = DirReader::new("/tmp").name(); ``` -------------------------------- ### RequestedAttributes::with_permissions(self) Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.RequestedAttributes.html Builder method to request the permissions attribute. ```APIDOC ## RequestedAttributes::with_permissions(self) ### Description Builder method to request the permissions attribute. ### Method `pub fn with_permissions(self) -> Self` ### Parameters - `self` - The current RequestedAttributes instance. ### Returns - `Self` - A new RequestedAttributes with `permissions` set to `true`. ### Example ```rust use getattrlistbulk::RequestedAttributes; let attrs = RequestedAttributes::default().with_permissions(); ``` ``` -------------------------------- ### DirReader::buffer_size Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Sets a custom buffer size for the directory read. ```APIDOC ## DirReader::buffer_size ### Description Sets a custom buffer size for the directory read. Larger buffers result in fewer syscalls but use more memory. Default is 64KB. ### Method buffer_size ### Signature `pub fn buffer_size(mut self, size: usize) -> Self` ### Parameters - **size** (usize) - Required - The buffer size in bytes. Recommended values: 64KB (default), 256KB for large directories (10,000+ files), 1MB for very large directories (100,000+ files). ### Returns - **Self** - The builder instance with the buffer size set. ### Example rust let reader = DirReader::new("/tmp").buffer_size(128 * 1024); ``` -------------------------------- ### DirReader::entry_count Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests entry counts (for directories) in the directory read. ```APIDOC ## DirReader::entry_count ### Description Requests entry counts (for directories) in the directory read. ### Method entry_count ### Signature `pub fn entry_count(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with the entry_count attribute requested. ### Example rust let reader = DirReader::new("/tmp").entry_count(); ``` -------------------------------- ### DirReader::buffer_size Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Sets a custom buffer size. Larger buffers result in fewer syscalls but use more memory. Default is 64KB. ```APIDOC ## DirReader::buffer_size ### Description Sets a custom buffer size. Larger buffers result in fewer syscalls but use more memory. Default is 64KB. ### Method buffer_size ### Parameters - **size** (usize) - Required - The buffer size in bytes. ### Returns - **Self** - The `DirReader` instance with the specified buffer size. ``` -------------------------------- ### read_i64, read_attribute_set, parse_attrreference_string, and parse_timespec methods Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/parser.rs.html These methods read signed 64-bit integers, attribute sets, attribute reference strings, and timespec structs from the buffer. They handle bounds checking and convert integer values into the corresponding Rust types. ```rust fn read_i64(&self, offset: usize) -> Result { if offset + 8 > self.buffer.len() { return Err(ParseError::UnexpectedEnd); } let bytes: [u8; 8] = self.buffer[offset..offset + 8] .try_into() .map_err(|_| ParseError::UnexpectedEnd)?; Ok(i64::from_ne_bytes(bytes)) } ``` ```rust fn read_attribute_set(&self, offset: usize) -> Result { let size = std::mem::size_of::(); if offset + size > self.buffer.len() { return Err(ParseError::UnexpectedEnd); } Ok(ffi::attribute_set { commonattr: self.read_u32(offset)?, volattr: self.read_u32(offset + 4)?, dirattr: self.read_u32(offset + 8)?, fileattr: self.read_u32(offset + 12)?, forkattr: self.read_u32(offset + 16)?, }) } ``` ```rust fn parse_attrreference_string( &self, _entry_start: usize, ref_offset: usize, ) -> Result<(String, usize), ParseError> { // Read attrreference: offset (i32) + length (u32) let data_offset = self.read_i32(ref_offset)?; let data_length = self.read_u32(ref_offset + 4)?; // Offset is relative to the attrreference location let string_start = (ref_offset as i32 + data_offset) as usize; let string_end = string_start + data_length as usize; // Bounds check if string_end > self.buffer.len() { return Err(ParseError::InvalidOffset); } // Extract string (excluding null terminator if present) let mut name_bytes = &self.buffer[string_start..string_end]; if let Some(null_pos) = name_bytes.iter().position(|&b| b == 0) { name_bytes = &name_bytes[..null_pos]; } // Convert to UTF-8, using lossy conversion for invalid sequences let name = String::from_utf8_lossy(name_bytes).into_owned(); // Return the offset after the attrreference (8 bytes) Ok((name, ref_offset + 8)) } ``` ```rust fn parse_timespec(&self, offset: usize) -> Result<(SystemTime, usize), ParseError> { // timespec is: tv_sec (i64) + tv_nsec (i64) on 64-bit let tv_sec = self.read_i64(offset)?; let tv_nsec = self.read_i64(offset + 8)?; let duration = Duration::new(tv_sec as u64, tv_nsec as u32); let time = UNIX_EPOCH + duration; Ok((time, offset + 16)) } } ``` -------------------------------- ### DirReader::read Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Reads the directory and returns an iterator over entries. The `name` attribute is always requested regardless of configuration, as entries without names are not useful. If you explicitly set `name: false`, it will be overridden to `true`. ```APIDOC ## DirReader::read ### Description Reads the directory and returns an iterator over entries. The `name` attribute is always requested regardless of configuration, as entries without names are not useful. If you explicitly set `name: false`, it will be overridden to `true`. ### Method read ### Returns - **Result** - An iterator over directory entries, or an error if the read fails. ``` -------------------------------- ### DirReader::inode Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests inode numbers. ```APIDOC ## DirReader::inode ### Description Requests inode numbers. ### Method inode ### Returns - **Self** - The `DirReader` instance with the inode attribute requested. ``` -------------------------------- ### DirReader::object_type Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Requests object types (file, directory, symlink, etc.). ```APIDOC ## DirReader::object_type ### Description Requests object types (file, directory, symlink, etc.). ### Method object_type ### Returns - **Self** - The `DirReader` instance with the object type attribute requested. ``` -------------------------------- ### Drop implementation for DirEntries Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Implements the Drop trait to close the directory file descriptor using libc::close when the DirEntries is dropped. ```rust impl Drop for DirEntries { fn drop(&mut self) { unsafe { libc::close(self.dirfd); } } } ``` -------------------------------- ### Unsafe Send implementation for DirEntries Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Implements Send for DirEntries, allowing it to be moved between threads. ```rust unsafe impl Send for DirEntries {} ``` -------------------------------- ### RequestedAttributes struct definition Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.RequestedAttributes.html Defines the RequestedAttributes struct with boolean fields for each attribute that can be requested for a directory entry. Set fields to true to request those attributes; only requested attributes are retrieved, improving performance. ```rust pub struct RequestedAttributes { pub name: bool, pub object_type: bool, pub size: bool, pub alloc_size: bool, pub modified_time: bool, pub permissions: bool, pub inode: bool, pub entry_count: bool, } ``` -------------------------------- ### DirReader::attributes Source: https://docs.rs/getattrlistbulk/latest/getattrlistbulk/struct.DirReader.html Sets custom attributes to request. ```APIDOC ## DirReader::attributes ### Description Sets custom attributes to request. ### Method attributes ### Parameters - **attrs** (RequestedAttributes) - Required - The attributes to request. ### Returns - **Self** - The `DirReader` instance with the specified attributes. ``` -------------------------------- ### DirReader::inode Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/builder.rs.html Requests inode numbers in the directory read. ```APIDOC ## DirReader::inode ### Description Requests inode numbers in the directory read. ### Method inode ### Signature `pub fn inode(mut self) -> Self` ### Parameters None. ### Returns - **Self** - The builder instance with the inode attribute requested. ### Example rust let reader = DirReader::new("/tmp").inode(); ``` -------------------------------- ### DirEntries::find_valid_bytes method Source: https://docs.rs/getattrlistbulk/latest/src/getattrlistbulk/iter.rs.html Determines how many bytes in the buffer contain valid entries by parsing entry lengths. ```rust fn find_valid_bytes(&self) -> usize { let mut offset = 0; while offset + 4 <= self.buffer.len() { let entry_length = u32::from_ne_bytes( self.buffer[offset..offset + 4] .try_into() .unwrap_or([0; 4]), ) as usize; if entry_length == 0 { break; } if offset + entry_length > self.buffer.len() { break; } offset += entry_length; } offset } ```