diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 8dd2f9c02..abe3bd16c 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -796,13 +796,39 @@ struct ZSTDMT_CCtx_s { static void ZSTDMT_freeJobsTable(ZSTDMT_jobDescription* jobTable, U32 nbJobs, ZSTD_customMem cMem) { - U32 jobNb; if (jobTable == NULL) return; + ZSTDMT_job_table_destroy_sync(jobTable, nbJobs, sizeof(*jobTable)); + ZSTDMT_rust_job_table_free(jobTable, nbJobs, sizeof(*jobTable), cMem); +} + +int ZSTDMT_job_table_init_sync(void* jobTable, unsigned nbJobs, size_t jobSize) +{ + U32 jobNb; + int initError = 0; + BYTE* const table = (BYTE*)jobTable; + assert(jobSize == sizeof(ZSTDMT_jobDescription)); + if (jobTable == NULL) return 1; for (jobNb=0; jobNbjob_mutex, NULL); + initError |= ZSTD_pthread_cond_init(&job->job_cond, NULL); + } + return initError; +} + +void ZSTDMT_job_table_destroy_sync(void* jobTable, unsigned nbJobs, size_t jobSize) +{ + U32 jobNb; + BYTE* const table = (BYTE*)jobTable; + if (jobTable == NULL) return; + assert(jobSize == sizeof(ZSTDMT_jobDescription)); + for (jobNb=0; jobNbjob_mutex); + ZSTD_pthread_cond_destroy(&job->job_cond); } - ZSTD_customFree(jobTable, cMem); } /* ZSTDMT_allocJobsTable() @@ -810,20 +836,11 @@ static void ZSTDMT_freeJobsTable(ZSTDMT_jobDescription* jobTable, U32 nbJobs, ZS * update *nbJobsPtr to next power of 2 value, as size of table */ static ZSTDMT_jobDescription* ZSTDMT_createJobsTable(U32* nbJobsPtr, ZSTD_customMem cMem) { - U32 const nbJobsLog2 = ZSTD_highbit32(*nbJobsPtr) + 1; - U32 const nbJobs = 1 << nbJobsLog2; - U32 jobNb; ZSTDMT_jobDescription* const jobTable = (ZSTDMT_jobDescription*) - ZSTD_customCalloc(nbJobs * sizeof(ZSTDMT_jobDescription), cMem); - int initError = 0; - if (jobTable==NULL) return NULL; - *nbJobsPtr = nbJobs; - for (jobNb=0; jobNb(len: usize) -> Option { mem::size_of::().checked_mul(len) } +fn rounded_job_count(requested: c_uint) -> Option { + if requested == 0 { + return None; + } + // The original C expression is `1 << (highbit(requested) + 1)`, which + // deliberately chooses a strictly larger power of two when requested is + // already a power of two. + let shift = usize::BITS - (requested as usize).leading_zeros(); + let count = 1usize.checked_shl(shift)?; + if count > c_uint::MAX as usize { + return None; + } + Some(count as c_uint) +} + +unsafe fn create_job_table( + nb_jobs_ptr: *mut c_uint, + job_size: usize, + custom_mem: ZstdCustomMem, +) -> *mut c_void { + if nb_jobs_ptr.is_null() || job_size == 0 { + return ptr::null_mut(); + } + let Some(nb_jobs) = (unsafe { rounded_job_count(*nb_jobs_ptr) }) else { + return ptr::null_mut(); + }; + let Some(table_size) = job_size.checked_mul(nb_jobs as usize) else { + return ptr::null_mut(); + }; + let table = unsafe { custom_calloc(table_size, custom_mem) }; + if table.is_null() { + return ptr::null_mut(); + } + + unsafe { *nb_jobs_ptr = nb_jobs }; + table +} + +unsafe fn free_job_table_storage(job_table: *mut c_void, custom_mem: ZstdCustomMem) { + if job_table.is_null() { + return; + } + unsafe { custom_free(job_table, custom_mem) }; +} + +#[no_mangle] +pub unsafe extern "C" fn ZSTDMT_rust_job_table_create( + nb_jobs_ptr: *mut c_uint, + job_size: usize, + custom_mem: ZstdCustomMem, +) -> *mut c_void { + unsafe { create_job_table(nb_jobs_ptr, job_size, custom_mem) } +} + +#[no_mangle] +pub unsafe extern "C" fn ZSTDMT_rust_job_table_free( + job_table: *mut c_void, + _nb_jobs: c_uint, + _job_size: usize, + custom_mem: ZstdCustomMem, +) { + // The MT C adapter destroys its platform mutexes and condition variables + // before calling this storage-only release function. Keeping this Rust + // side free of MT-only C references also preserves single-threaded builds + // where zstdmt_compress.c is intentionally omitted. + unsafe { free_job_table_storage(job_table, custom_mem) } +} + unsafe fn create_buffer_pool( max_nb_buffers: usize, custom_mem: ZstdCustomMem, @@ -601,4 +670,14 @@ mod tests { ZSTDMT_rust_buffer_pool_free(pool); } } + + #[test] + fn job_table_count_matches_c_power_of_two_contract() { + assert_eq!(rounded_job_count(0), None); + assert_eq!(rounded_job_count(1), Some(2)); + assert_eq!(rounded_job_count(3), Some(4)); + assert_eq!(rounded_job_count(4), Some(8)); + assert_eq!(rounded_job_count(255), Some(256)); + assert_eq!(rounded_job_count(256), Some(512)); + } }