feat(compress): move workspace lifecycle orchestration to Rust
Route private workspace init, dynamic allocation, release, and move ordering through a Rust lifecycle bridge. C continues to own the opaque workspace layout, sanitizer-sensitive byte operations, custom allocator callbacks, and consistency assertions; Rust owns publication order, allocation-before-initialization, metadata-zero-before-release, and source invalidation. The checked projection keeps the field-slot and callback ABI explicit and adds focused lifecycle tests for successful and failed allocation, free ordering, and move semantics. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo check --manifest-path rust/Cargo.toml --tests - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1 - ulimit -v 41943040; make -j1 -C tests test
This commit is contained in:
+155
-22
@@ -168,6 +168,75 @@ typedef struct {
|
||||
ZSTD_cwksp_static_alloc_e isStatic;
|
||||
} ZSTD_cwksp;
|
||||
|
||||
/* The workspace layout remains private to C. Rust receives field slots for
|
||||
* initialization and opaque C callbacks for byte-level operations, allocator
|
||||
* ownership, and sanitizer bookkeeping. */
|
||||
typedef void* (*ZSTD_rust_cwksp_allocate_f)(void* context, size_t size);
|
||||
typedef void (*ZSTD_rust_cwksp_release_f)(
|
||||
void* context, void* workspace, size_t workspaceSize);
|
||||
typedef void (*ZSTD_rust_cwksp_callback_f)(void* context);
|
||||
typedef void (*ZSTD_rust_cwksp_copy_f)(void* destination, const void* source);
|
||||
typedef struct {
|
||||
void* callbackContext;
|
||||
void** workspace;
|
||||
void** workspaceEnd;
|
||||
void** objectEnd;
|
||||
void** tableEnd;
|
||||
void** tableValidEnd;
|
||||
void** allocStart;
|
||||
void** initOnceStart;
|
||||
BYTE* allocFailed;
|
||||
int* workspaceOversizedDuration;
|
||||
int* phase;
|
||||
int* isStatic;
|
||||
void* allocatorContext;
|
||||
ZSTD_rust_cwksp_allocate_f allocate;
|
||||
void* releaseContext;
|
||||
ZSTD_rust_cwksp_release_f release;
|
||||
ZSTD_rust_cwksp_callback_f clear;
|
||||
ZSTD_rust_cwksp_callback_f zero;
|
||||
} ZSTD_rust_cwkspState;
|
||||
typedef char ZSTD_rust_cwksp_enum_layout[
|
||||
(sizeof(ZSTD_cwksp_alloc_phase_e) == sizeof(int)
|
||||
&& sizeof(ZSTD_cwksp_static_alloc_e) == sizeof(int))
|
||||
? 1 : -1];
|
||||
typedef char ZSTD_rust_cwksp_constants_layout[
|
||||
(ZSTD_CWKSP_ALIGNMENT_BYTES == 64) ? 1 : -1];
|
||||
typedef char ZSTD_rust_cwksp_state_layout[
|
||||
(offsetof(ZSTD_rust_cwkspState, callbackContext) == 0
|
||||
&& offsetof(ZSTD_rust_cwkspState, workspace) == sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, workspaceEnd) == 2 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, objectEnd) == 3 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, tableEnd) == 4 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, tableValidEnd) == 5 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, allocStart) == 6 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, initOnceStart) == 7 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, allocFailed) == 8 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, workspaceOversizedDuration)
|
||||
== 9 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, phase) == 10 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, isStatic) == 11 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, allocatorContext)
|
||||
== 12 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, allocate) == 13 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, releaseContext)
|
||||
== 14 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, release) == 15 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, clear) == 16 * sizeof(void*)
|
||||
&& offsetof(ZSTD_rust_cwkspState, zero) == 17 * sizeof(void*)
|
||||
&& sizeof(ZSTD_rust_cwkspState) == 18 * sizeof(void*))
|
||||
? 1 : -1];
|
||||
size_t ZSTD_rust_cwkspInit(
|
||||
const ZSTD_rust_cwkspState* state,
|
||||
void* start, size_t size, int isStatic);
|
||||
size_t ZSTD_rust_cwkspCreate(
|
||||
const ZSTD_rust_cwkspState* state, size_t size);
|
||||
void ZSTD_rust_cwkspFree(const ZSTD_rust_cwkspState* state);
|
||||
void ZSTD_rust_cwkspMove(
|
||||
void* destination, void* source,
|
||||
ZSTD_rust_cwksp_copy_f copy,
|
||||
ZSTD_rust_cwksp_callback_f zero);
|
||||
|
||||
/*-*************************************
|
||||
* Functions
|
||||
***************************************/
|
||||
@@ -657,6 +726,68 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) {
|
||||
ZSTD_cwksp_assert_internal_consistency(ws);
|
||||
}
|
||||
|
||||
MEM_STATIC void ZSTD_cwksp_rust_clear(void* context)
|
||||
{
|
||||
ZSTD_cwksp_clear((ZSTD_cwksp*)context);
|
||||
}
|
||||
|
||||
MEM_STATIC void ZSTD_cwksp_rust_zero(void* context)
|
||||
{
|
||||
ZSTD_memset(context, 0, sizeof(ZSTD_cwksp));
|
||||
}
|
||||
|
||||
MEM_STATIC void ZSTD_cwksp_rust_copy(void* destination, const void* source)
|
||||
{
|
||||
ZSTD_memcpy(destination, source, sizeof(ZSTD_cwksp));
|
||||
}
|
||||
|
||||
MEM_STATIC void* ZSTD_cwksp_rust_allocate(void* context, size_t size)
|
||||
{
|
||||
void* const workspace = ZSTD_customMalloc(
|
||||
size, *(const ZSTD_customMem*)context);
|
||||
if (workspace != NULL) {
|
||||
assert(((size_t)workspace & (sizeof(void*) - 1)) == 0);
|
||||
}
|
||||
return workspace;
|
||||
}
|
||||
|
||||
MEM_STATIC void ZSTD_cwksp_rust_release(
|
||||
void* context, void* workspace, size_t workspaceSize)
|
||||
{
|
||||
ZSTD_customMem const customMem = *(const ZSTD_customMem*)context;
|
||||
(void)workspaceSize;
|
||||
#if ZSTD_MEMORY_SANITIZER && !defined(ZSTD_MSAN_DONT_POISON_WORKSPACE)
|
||||
if (workspace != NULL && customMem.customFree != NULL) {
|
||||
__msan_unpoison(workspace, workspaceSize);
|
||||
}
|
||||
#endif
|
||||
ZSTD_customFree(workspace, customMem);
|
||||
}
|
||||
|
||||
MEM_STATIC ZSTD_rust_cwkspState ZSTD_cwksp_rust_state(ZSTD_cwksp* ws)
|
||||
{
|
||||
ZSTD_rust_cwkspState state;
|
||||
state.callbackContext = ws;
|
||||
state.workspace = &ws->workspace;
|
||||
state.workspaceEnd = &ws->workspaceEnd;
|
||||
state.objectEnd = &ws->objectEnd;
|
||||
state.tableEnd = &ws->tableEnd;
|
||||
state.tableValidEnd = &ws->tableValidEnd;
|
||||
state.allocStart = &ws->allocStart;
|
||||
state.initOnceStart = &ws->initOnceStart;
|
||||
state.allocFailed = &ws->allocFailed;
|
||||
state.workspaceOversizedDuration = &ws->workspaceOversizedDuration;
|
||||
state.phase = (int*)&ws->phase;
|
||||
state.isStatic = (int*)&ws->isStatic;
|
||||
state.allocatorContext = NULL;
|
||||
state.allocate = NULL;
|
||||
state.releaseContext = NULL;
|
||||
state.release = NULL;
|
||||
state.clear = ZSTD_cwksp_rust_clear;
|
||||
state.zero = ZSTD_cwksp_rust_zero;
|
||||
return state;
|
||||
}
|
||||
|
||||
MEM_STATIC size_t ZSTD_cwksp_sizeof(const ZSTD_cwksp* ws) {
|
||||
return (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->workspace);
|
||||
}
|
||||
@@ -674,36 +805,38 @@ MEM_STATIC size_t ZSTD_cwksp_used(const ZSTD_cwksp* ws) {
|
||||
MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size, ZSTD_cwksp_static_alloc_e isStatic) {
|
||||
DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size);
|
||||
assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */
|
||||
ws->workspace = start;
|
||||
ws->workspaceEnd = (BYTE*)start + size;
|
||||
ws->objectEnd = ws->workspace;
|
||||
ws->tableValidEnd = ws->objectEnd;
|
||||
ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws);
|
||||
ws->phase = ZSTD_cwksp_alloc_objects;
|
||||
ws->isStatic = isStatic;
|
||||
ZSTD_cwksp_clear(ws);
|
||||
ws->workspaceOversizedDuration = 0;
|
||||
{ ZSTD_rust_cwkspState const state = ZSTD_cwksp_rust_state(ws);
|
||||
size_t const result = ZSTD_rust_cwkspInit(
|
||||
&state, start, size, (int)isStatic);
|
||||
assert(!ZSTD_isError(result));
|
||||
(void)result;
|
||||
}
|
||||
ZSTD_cwksp_assert_internal_consistency(ws);
|
||||
}
|
||||
|
||||
MEM_STATIC size_t ZSTD_cwksp_create(ZSTD_cwksp* ws, size_t size, ZSTD_customMem customMem) {
|
||||
void* workspace = ZSTD_customMalloc(size, customMem);
|
||||
DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size);
|
||||
RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!");
|
||||
ZSTD_cwksp_init(ws, workspace, size, ZSTD_cwksp_dynamic_alloc);
|
||||
return 0;
|
||||
{ ZSTD_rust_cwkspState state = ZSTD_cwksp_rust_state(ws);
|
||||
state.allocatorContext = &customMem;
|
||||
state.allocate = ZSTD_cwksp_rust_allocate;
|
||||
state.releaseContext = &customMem;
|
||||
state.release = ZSTD_cwksp_rust_release;
|
||||
{ size_t const result = ZSTD_rust_cwkspCreate(&state, size);
|
||||
if (!ZSTD_isError(result)) {
|
||||
ZSTD_cwksp_assert_internal_consistency(ws);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_STATIC void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) {
|
||||
void *ptr = ws->workspace;
|
||||
DEBUGLOG(4, "cwksp: freeing workspace");
|
||||
#if ZSTD_MEMORY_SANITIZER && !defined(ZSTD_MSAN_DONT_POISON_WORKSPACE)
|
||||
if (ptr != NULL && customMem.customFree != NULL) {
|
||||
__msan_unpoison(ptr, ZSTD_cwksp_sizeof(ws));
|
||||
{ ZSTD_rust_cwkspState state = ZSTD_cwksp_rust_state(ws);
|
||||
state.releaseContext = &customMem;
|
||||
state.release = ZSTD_cwksp_rust_release;
|
||||
ZSTD_rust_cwkspFree(&state);
|
||||
}
|
||||
#endif
|
||||
ZSTD_memset(ws, 0, sizeof(ZSTD_cwksp));
|
||||
ZSTD_customFree(ptr, customMem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,8 +844,8 @@ MEM_STATIC void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) {
|
||||
* is left in an invalid state (src must be re-init()'ed before it's used again).
|
||||
*/
|
||||
MEM_STATIC void ZSTD_cwksp_move(ZSTD_cwksp* dst, ZSTD_cwksp* src) {
|
||||
*dst = *src;
|
||||
ZSTD_memset(src, 0, sizeof(ZSTD_cwksp));
|
||||
ZSTD_rust_cwkspMove(
|
||||
dst, src, ZSTD_cwksp_rust_copy, ZSTD_cwksp_rust_zero);
|
||||
}
|
||||
|
||||
MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) {
|
||||
|
||||
Reference in New Issue
Block a user