Path: blob/master/Utilities/cmzstd/lib/compress/zstd_cwksp.h
5028 views
/*1* Copyright (c) Meta Platforms, Inc. and affiliates.2* All rights reserved.3*4* This source code is licensed under both the BSD-style license (found in the5* LICENSE file in the root directory of this source tree) and the GPLv2 (found6* in the COPYING file in the root directory of this source tree).7* You may select, at your option, one of the above-listed licenses.8*/910#ifndef ZSTD_CWKSP_H11#define ZSTD_CWKSP_H1213/*-*************************************14* Dependencies15***************************************/16#include "../common/allocations.h" /* ZSTD_customMalloc, ZSTD_customFree */17#include "../common/zstd_internal.h"18#include "../common/portability_macros.h"19#include "../common/compiler.h" /* ZS2_isPower2 */2021/*-*************************************22* Constants23***************************************/2425/* Since the workspace is effectively its own little malloc implementation /26* arena, when we run under ASAN, we should similarly insert redzones between27* each internal element of the workspace, so ASAN will catch overruns that28* reach outside an object but that stay inside the workspace.29*30* This defines the size of that redzone.31*/32#ifndef ZSTD_CWKSP_ASAN_REDZONE_SIZE33#define ZSTD_CWKSP_ASAN_REDZONE_SIZE 12834#endif353637/* Set our tables and aligneds to align by 64 bytes */38#define ZSTD_CWKSP_ALIGNMENT_BYTES 643940/*-*************************************41* Structures42***************************************/43typedef enum {44ZSTD_cwksp_alloc_objects,45ZSTD_cwksp_alloc_aligned_init_once,46ZSTD_cwksp_alloc_aligned,47ZSTD_cwksp_alloc_buffers48} ZSTD_cwksp_alloc_phase_e;4950/**51* Used to describe whether the workspace is statically allocated (and will not52* necessarily ever be freed), or if it's dynamically allocated and we can53* expect a well-formed caller to free this.54*/55typedef enum {56ZSTD_cwksp_dynamic_alloc,57ZSTD_cwksp_static_alloc58} ZSTD_cwksp_static_alloc_e;5960/**61* Zstd fits all its internal datastructures into a single continuous buffer,62* so that it only needs to perform a single OS allocation (or so that a buffer63* can be provided to it and it can perform no allocations at all). This buffer64* is called the workspace.65*66* Several optimizations complicate that process of allocating memory ranges67* from this workspace for each internal datastructure:68*69* - These different internal datastructures have different setup requirements:70*71* - The static objects need to be cleared once and can then be trivially72* reused for each compression.73*74* - Various buffers don't need to be initialized at all--they are always75* written into before they're read.76*77* - The matchstate tables have a unique requirement that they don't need78* their memory to be totally cleared, but they do need the memory to have79* some bound, i.e., a guarantee that all values in the memory they've been80* allocated is less than some maximum value (which is the starting value81* for the indices that they will then use for compression). When this82* guarantee is provided to them, they can use the memory without any setup83* work. When it can't, they have to clear the area.84*85* - These buffers also have different alignment requirements.86*87* - We would like to reuse the objects in the workspace for multiple88* compressions without having to perform any expensive reallocation or89* reinitialization work.90*91* - We would like to be able to efficiently reuse the workspace across92* multiple compressions **even when the compression parameters change** and93* we need to resize some of the objects (where possible).94*95* To attempt to manage this buffer, given these constraints, the ZSTD_cwksp96* abstraction was created. It works as follows:97*98* Workspace Layout:99*100* [ ... workspace ... ]101* [objects][tables ->] free space [<- buffers][<- aligned][<- init once]102*103* The various objects that live in the workspace are divided into the104* following categories, and are allocated separately:105*106* - Static objects: this is optionally the enclosing ZSTD_CCtx or ZSTD_CDict,107* so that literally everything fits in a single buffer. Note: if present,108* this must be the first object in the workspace, since ZSTD_customFree{CCtx,109* CDict}() rely on a pointer comparison to see whether one or two frees are110* required.111*112* - Fixed size objects: these are fixed-size, fixed-count objects that are113* nonetheless "dynamically" allocated in the workspace so that we can114* control how they're initialized separately from the broader ZSTD_CCtx.115* Examples:116* - Entropy Workspace117* - 2 x ZSTD_compressedBlockState_t118* - CDict dictionary contents119*120* - Tables: these are any of several different datastructures (hash tables,121* chain tables, binary trees) that all respect a common format: they are122* uint32_t arrays, all of whose values are between 0 and (nextSrc - base).123* Their sizes depend on the cparams. These tables are 64-byte aligned.124*125* - Init once: these buffers require to be initialized at least once before126* use. They should be used when we want to skip memory initialization127* while not triggering memory checkers (like Valgrind) when reading from128* from this memory without writing to it first.129* These buffers should be used carefully as they might contain data130* from previous compressions.131* Buffers are aligned to 64 bytes.132*133* - Aligned: these buffers don't require any initialization before they're134* used. The user of the buffer should make sure they write into a buffer135* location before reading from it.136* Buffers are aligned to 64 bytes.137*138* - Buffers: these buffers are used for various purposes that don't require139* any alignment or initialization before they're used. This means they can140* be moved around at no cost for a new compression.141*142* Allocating Memory:143*144* The various types of objects must be allocated in order, so they can be145* correctly packed into the workspace buffer. That order is:146*147* 1. Objects148* 2. Init once / Tables149* 3. Aligned / Tables150* 4. Buffers / Tables151*152* Attempts to reserve objects of different types out of order will fail.153*/154typedef struct {155void* workspace;156void* workspaceEnd;157158void* objectEnd;159void* tableEnd;160void* tableValidEnd;161void* allocStart;162void* initOnceStart;163164BYTE allocFailed;165int workspaceOversizedDuration;166ZSTD_cwksp_alloc_phase_e phase;167ZSTD_cwksp_static_alloc_e isStatic;168} ZSTD_cwksp;169170/*-*************************************171* Functions172***************************************/173174MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws);175MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws);176177MEM_STATIC void ZSTD_cwksp_assert_internal_consistency(ZSTD_cwksp* ws) {178(void)ws;179assert(ws->workspace <= ws->objectEnd);180assert(ws->objectEnd <= ws->tableEnd);181assert(ws->objectEnd <= ws->tableValidEnd);182assert(ws->tableEnd <= ws->allocStart);183assert(ws->tableValidEnd <= ws->allocStart);184assert(ws->allocStart <= ws->workspaceEnd);185assert(ws->initOnceStart <= ZSTD_cwksp_initialAllocStart(ws));186assert(ws->workspace <= ws->initOnceStart);187#if ZSTD_MEMORY_SANITIZER188{189intptr_t const offset = __msan_test_shadow(ws->initOnceStart,190(U8*)ZSTD_cwksp_initialAllocStart(ws) - (U8*)ws->initOnceStart);191(void)offset;192#if defined(ZSTD_MSAN_PRINT)193if(offset!=-1) {194__msan_print_shadow((U8*)ws->initOnceStart + offset - 8, 32);195}196#endif197assert(offset==-1);198};199#endif200}201202/**203* Align must be a power of 2.204*/205MEM_STATIC size_t ZSTD_cwksp_align(size_t size, size_t align) {206size_t const mask = align - 1;207assert(ZSTD_isPower2(align));208return (size + mask) & ~mask;209}210211/**212* Use this to determine how much space in the workspace we will consume to213* allocate this object. (Normally it should be exactly the size of the object,214* but under special conditions, like ASAN, where we pad each object, it might215* be larger.)216*217* Since tables aren't currently redzoned, you don't need to call through this218* to figure out how much space you need for the matchState tables. Everything219* else is though.220*221* Do not use for sizing aligned buffers. Instead, use ZSTD_cwksp_aligned64_alloc_size().222*/223MEM_STATIC size_t ZSTD_cwksp_alloc_size(size_t size) {224if (size == 0)225return 0;226#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)227return size + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;228#else229return size;230#endif231}232233MEM_STATIC size_t ZSTD_cwksp_aligned_alloc_size(size_t size, size_t alignment) {234return ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(size, alignment));235}236237/**238* Returns an adjusted alloc size that is the nearest larger multiple of 64 bytes.239* Used to determine the number of bytes required for a given "aligned".240*/241MEM_STATIC size_t ZSTD_cwksp_aligned64_alloc_size(size_t size) {242return ZSTD_cwksp_aligned_alloc_size(size, ZSTD_CWKSP_ALIGNMENT_BYTES);243}244245/**246* Returns the amount of additional space the cwksp must allocate247* for internal purposes (currently only alignment).248*/249MEM_STATIC size_t ZSTD_cwksp_slack_space_required(void) {250/* For alignment, the wksp will always allocate an additional 2*ZSTD_CWKSP_ALIGNMENT_BYTES251* bytes to align the beginning of tables section and end of buffers;252*/253size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES * 2;254return slackSpace;255}256257258/**259* Return the number of additional bytes required to align a pointer to the given number of bytes.260* alignBytes must be a power of two.261*/262MEM_STATIC size_t ZSTD_cwksp_bytes_to_align_ptr(void* ptr, const size_t alignBytes) {263size_t const alignBytesMask = alignBytes - 1;264size_t const bytes = (alignBytes - ((size_t)ptr & (alignBytesMask))) & alignBytesMask;265assert(ZSTD_isPower2(alignBytes));266assert(bytes < alignBytes);267return bytes;268}269270/**271* Returns the initial value for allocStart which is used to determine the position from272* which we can allocate from the end of the workspace.273*/274MEM_STATIC void* ZSTD_cwksp_initialAllocStart(ZSTD_cwksp* ws)275{276char* endPtr = (char*)ws->workspaceEnd;277assert(ZSTD_isPower2(ZSTD_CWKSP_ALIGNMENT_BYTES));278endPtr = endPtr - ((size_t)endPtr % ZSTD_CWKSP_ALIGNMENT_BYTES);279return (void*)endPtr;280}281282/**283* Internal function. Do not use directly.284* Reserves the given number of bytes within the aligned/buffer segment of the wksp,285* which counts from the end of the wksp (as opposed to the object/table segment).286*287* Returns a pointer to the beginning of that space.288*/289MEM_STATIC void*290ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, size_t const bytes)291{292void* const alloc = (BYTE*)ws->allocStart - bytes;293void* const bottom = ws->tableEnd;294DEBUGLOG(5, "cwksp: reserving [0x%p]:%zd bytes; %zd bytes remaining",295alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);296ZSTD_cwksp_assert_internal_consistency(ws);297assert(alloc >= bottom);298if (alloc < bottom) {299DEBUGLOG(4, "cwksp: alloc failed!");300ws->allocFailed = 1;301return NULL;302}303/* the area is reserved from the end of wksp.304* If it overlaps with tableValidEnd, it voids guarantees on values' range */305if (alloc < ws->tableValidEnd) {306ws->tableValidEnd = alloc;307}308ws->allocStart = alloc;309return alloc;310}311312/**313* Moves the cwksp to the next phase, and does any necessary allocations.314* cwksp initialization must necessarily go through each phase in order.315* Returns a 0 on success, or zstd error316*/317MEM_STATIC size_t318ZSTD_cwksp_internal_advance_phase(ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase)319{320assert(phase >= ws->phase);321if (phase > ws->phase) {322/* Going from allocating objects to allocating initOnce / tables */323if (ws->phase < ZSTD_cwksp_alloc_aligned_init_once &&324phase >= ZSTD_cwksp_alloc_aligned_init_once) {325ws->tableValidEnd = ws->objectEnd;326ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws);327328{ /* Align the start of the tables to 64 bytes. Use [0, 63] bytes */329void *const alloc = ws->objectEnd;330size_t const bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, ZSTD_CWKSP_ALIGNMENT_BYTES);331void *const objectEnd = (BYTE *) alloc + bytesToAlign;332DEBUGLOG(5, "reserving table alignment addtl space: %zu", bytesToAlign);333RETURN_ERROR_IF(objectEnd > ws->workspaceEnd, memory_allocation,334"table phase - alignment initial allocation failed!");335ws->objectEnd = objectEnd;336ws->tableEnd = objectEnd; /* table area starts being empty */337if (ws->tableValidEnd < ws->tableEnd) {338ws->tableValidEnd = ws->tableEnd;339}340}341}342ws->phase = phase;343ZSTD_cwksp_assert_internal_consistency(ws);344}345return 0;346}347348/**349* Returns whether this object/buffer/etc was allocated in this workspace.350*/351MEM_STATIC int ZSTD_cwksp_owns_buffer(const ZSTD_cwksp* ws, const void* ptr)352{353return (ptr != NULL) && (ws->workspace <= ptr) && (ptr < ws->workspaceEnd);354}355356/**357* Internal function. Do not use directly.358*/359MEM_STATIC void*360ZSTD_cwksp_reserve_internal(ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase)361{362void* alloc;363if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase)) || bytes == 0) {364return NULL;365}366367#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)368/* over-reserve space */369bytes += 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;370#endif371372alloc = ZSTD_cwksp_reserve_internal_buffer_space(ws, bytes);373374#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)375/* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on376* either size. */377if (alloc) {378alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;379if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {380/* We need to keep the redzone poisoned while unpoisoning the bytes that381* are actually allocated. */382__asan_unpoison_memory_region(alloc, bytes - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE);383}384}385#endif386387return alloc;388}389390/**391* Reserves and returns unaligned memory.392*/393MEM_STATIC BYTE* ZSTD_cwksp_reserve_buffer(ZSTD_cwksp* ws, size_t bytes)394{395return (BYTE*)ZSTD_cwksp_reserve_internal(ws, bytes, ZSTD_cwksp_alloc_buffers);396}397398/**399* Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes).400* This memory has been initialized at least once in the past.401* This doesn't mean it has been initialized this time, and it might contain data from previous402* operations.403* The main usage is for algorithms that might need read access into uninitialized memory.404* The algorithm must maintain safety under these conditions and must make sure it doesn't405* leak any of the past data (directly or in side channels).406*/407MEM_STATIC void* ZSTD_cwksp_reserve_aligned_init_once(ZSTD_cwksp* ws, size_t bytes)408{409size_t const alignedBytes = ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES);410void* ptr = ZSTD_cwksp_reserve_internal(ws, alignedBytes, ZSTD_cwksp_alloc_aligned_init_once);411assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1)) == 0);412if(ptr && ptr < ws->initOnceStart) {413/* We assume the memory following the current allocation is either:414* 1. Not usable as initOnce memory (end of workspace)415* 2. Another initOnce buffer that has been allocated before (and so was previously memset)416* 3. An ASAN redzone, in which case we don't want to write on it417* For these reasons it should be fine to not explicitly zero every byte up to ws->initOnceStart.418* Note that we assume here that MSAN and ASAN cannot run in the same time. */419ZSTD_memset(ptr, 0, MIN((size_t)((U8*)ws->initOnceStart - (U8*)ptr), alignedBytes));420ws->initOnceStart = ptr;421}422#if ZSTD_MEMORY_SANITIZER423assert(__msan_test_shadow(ptr, bytes) == -1);424#endif425return ptr;426}427428/**429* Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes).430*/431MEM_STATIC void* ZSTD_cwksp_reserve_aligned64(ZSTD_cwksp* ws, size_t bytes)432{433void* const ptr = ZSTD_cwksp_reserve_internal(ws,434ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES),435ZSTD_cwksp_alloc_aligned);436assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1)) == 0);437return ptr;438}439440/**441* Aligned on 64 bytes. These buffers have the special property that442* their values remain constrained, allowing us to reuse them without443* memset()-ing them.444*/445MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes)446{447const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned_init_once;448void* alloc;449void* end;450void* top;451452/* We can only start allocating tables after we are done reserving space for objects at the453* start of the workspace */454if(ws->phase < phase) {455if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) {456return NULL;457}458}459alloc = ws->tableEnd;460end = (BYTE *)alloc + bytes;461top = ws->allocStart;462463DEBUGLOG(5, "cwksp: reserving %p table %zd bytes, %zd bytes remaining",464alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);465assert((bytes & (sizeof(U32)-1)) == 0);466ZSTD_cwksp_assert_internal_consistency(ws);467assert(end <= top);468if (end > top) {469DEBUGLOG(4, "cwksp: table alloc failed!");470ws->allocFailed = 1;471return NULL;472}473ws->tableEnd = end;474475#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)476if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {477__asan_unpoison_memory_region(alloc, bytes);478}479#endif480481assert((bytes & (ZSTD_CWKSP_ALIGNMENT_BYTES-1)) == 0);482assert(((size_t)alloc & (ZSTD_CWKSP_ALIGNMENT_BYTES-1)) == 0);483return alloc;484}485486/**487* Aligned on sizeof(void*).488* Note : should happen only once, at workspace first initialization489*/490MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes)491{492size_t const roundedBytes = ZSTD_cwksp_align(bytes, sizeof(void*));493void* alloc = ws->objectEnd;494void* end = (BYTE*)alloc + roundedBytes;495496#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)497/* over-reserve space */498end = (BYTE *)end + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;499#endif500501DEBUGLOG(4,502"cwksp: reserving %p object %zd bytes (rounded to %zd), %zd bytes remaining",503alloc, bytes, roundedBytes, ZSTD_cwksp_available_space(ws) - roundedBytes);504assert((size_t)alloc % ZSTD_ALIGNOF(void*) == 0);505assert(bytes % ZSTD_ALIGNOF(void*) == 0);506ZSTD_cwksp_assert_internal_consistency(ws);507/* we must be in the first phase, no advance is possible */508if (ws->phase != ZSTD_cwksp_alloc_objects || end > ws->workspaceEnd) {509DEBUGLOG(3, "cwksp: object alloc failed!");510ws->allocFailed = 1;511return NULL;512}513ws->objectEnd = end;514ws->tableEnd = end;515ws->tableValidEnd = end;516517#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)518/* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on519* either size. */520alloc = (BYTE*)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;521if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {522__asan_unpoison_memory_region(alloc, bytes);523}524#endif525526return alloc;527}528/**529* with alignment control530* Note : should happen only once, at workspace first initialization531*/532MEM_STATIC void* ZSTD_cwksp_reserve_object_aligned(ZSTD_cwksp* ws, size_t byteSize, size_t alignment)533{534size_t const mask = alignment - 1;535size_t const surplus = (alignment > sizeof(void*)) ? alignment - sizeof(void*) : 0;536void* const start = ZSTD_cwksp_reserve_object(ws, byteSize + surplus);537if (start == NULL) return NULL;538if (surplus == 0) return start;539assert(ZSTD_isPower2(alignment));540return (void*)(((size_t)start + surplus) & ~mask);541}542543MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws)544{545DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_dirty");546547#if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)548/* To validate that the table reuse logic is sound, and that we don't549* access table space that we haven't cleaned, we re-"poison" the table550* space every time we mark it dirty.551* Since tableValidEnd space and initOnce space may overlap we don't poison552* the initOnce portion as it break its promise. This means that this poisoning553* check isn't always applied fully. */554{555size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;556assert(__msan_test_shadow(ws->objectEnd, size) == -1);557if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) {558__msan_poison(ws->objectEnd, size);559} else {560assert(ws->initOnceStart >= ws->objectEnd);561__msan_poison(ws->objectEnd, (BYTE*)ws->initOnceStart - (BYTE*)ws->objectEnd);562}563}564#endif565566assert(ws->tableValidEnd >= ws->objectEnd);567assert(ws->tableValidEnd <= ws->allocStart);568ws->tableValidEnd = ws->objectEnd;569ZSTD_cwksp_assert_internal_consistency(ws);570}571572MEM_STATIC void ZSTD_cwksp_mark_tables_clean(ZSTD_cwksp* ws) {573DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_clean");574assert(ws->tableValidEnd >= ws->objectEnd);575assert(ws->tableValidEnd <= ws->allocStart);576if (ws->tableValidEnd < ws->tableEnd) {577ws->tableValidEnd = ws->tableEnd;578}579ZSTD_cwksp_assert_internal_consistency(ws);580}581582/**583* Zero the part of the allocated tables not already marked clean.584*/585MEM_STATIC void ZSTD_cwksp_clean_tables(ZSTD_cwksp* ws) {586DEBUGLOG(4, "cwksp: ZSTD_cwksp_clean_tables");587assert(ws->tableValidEnd >= ws->objectEnd);588assert(ws->tableValidEnd <= ws->allocStart);589if (ws->tableValidEnd < ws->tableEnd) {590ZSTD_memset(ws->tableValidEnd, 0, (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd));591}592ZSTD_cwksp_mark_tables_clean(ws);593}594595/**596* Invalidates table allocations.597* All other allocations remain valid.598*/599MEM_STATIC void ZSTD_cwksp_clear_tables(ZSTD_cwksp* ws)600{601DEBUGLOG(4, "cwksp: clearing tables!");602603#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)604/* We don't do this when the workspace is statically allocated, because605* when that is the case, we have no capability to hook into the end of the606* workspace's lifecycle to unpoison the memory.607*/608if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {609size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;610__asan_poison_memory_region(ws->objectEnd, size);611}612#endif613614ws->tableEnd = ws->objectEnd;615ZSTD_cwksp_assert_internal_consistency(ws);616}617618/**619* Invalidates all buffer, aligned, and table allocations.620* Object allocations remain valid.621*/622MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) {623DEBUGLOG(4, "cwksp: clearing!");624625#if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)626/* To validate that the context reuse logic is sound, and that we don't627* access stuff that this compression hasn't initialized, we re-"poison"628* the workspace except for the areas in which we expect memory reuse629* without initialization (objects, valid tables area and init once630* memory). */631{632if((BYTE*)ws->tableValidEnd < (BYTE*)ws->initOnceStart) {633size_t size = (BYTE*)ws->initOnceStart - (BYTE*)ws->tableValidEnd;634__msan_poison(ws->tableValidEnd, size);635}636}637#endif638639#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)640/* We don't do this when the workspace is statically allocated, because641* when that is the case, we have no capability to hook into the end of the642* workspace's lifecycle to unpoison the memory.643*/644if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {645size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->objectEnd;646__asan_poison_memory_region(ws->objectEnd, size);647}648#endif649650ws->tableEnd = ws->objectEnd;651ws->allocStart = ZSTD_cwksp_initialAllocStart(ws);652ws->allocFailed = 0;653if (ws->phase > ZSTD_cwksp_alloc_aligned_init_once) {654ws->phase = ZSTD_cwksp_alloc_aligned_init_once;655}656ZSTD_cwksp_assert_internal_consistency(ws);657}658659MEM_STATIC size_t ZSTD_cwksp_sizeof(const ZSTD_cwksp* ws) {660return (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->workspace);661}662663MEM_STATIC size_t ZSTD_cwksp_used(const ZSTD_cwksp* ws) {664return (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->workspace)665+ (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->allocStart);666}667668/**669* The provided workspace takes ownership of the buffer [start, start+size).670* Any existing values in the workspace are ignored (the previously managed671* buffer, if present, must be separately freed).672*/673MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size, ZSTD_cwksp_static_alloc_e isStatic) {674DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size);675assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */676ws->workspace = start;677ws->workspaceEnd = (BYTE*)start + size;678ws->objectEnd = ws->workspace;679ws->tableValidEnd = ws->objectEnd;680ws->initOnceStart = ZSTD_cwksp_initialAllocStart(ws);681ws->phase = ZSTD_cwksp_alloc_objects;682ws->isStatic = isStatic;683ZSTD_cwksp_clear(ws);684ws->workspaceOversizedDuration = 0;685ZSTD_cwksp_assert_internal_consistency(ws);686}687688MEM_STATIC size_t ZSTD_cwksp_create(ZSTD_cwksp* ws, size_t size, ZSTD_customMem customMem) {689void* workspace = ZSTD_customMalloc(size, customMem);690DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size);691RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!");692ZSTD_cwksp_init(ws, workspace, size, ZSTD_cwksp_dynamic_alloc);693return 0;694}695696MEM_STATIC void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) {697void *ptr = ws->workspace;698DEBUGLOG(4, "cwksp: freeing workspace");699#if ZSTD_MEMORY_SANITIZER && !defined(ZSTD_MSAN_DONT_POISON_WORKSPACE)700if (ptr != NULL && customMem.customFree != NULL) {701__msan_unpoison(ptr, ZSTD_cwksp_sizeof(ws));702}703#endif704ZSTD_memset(ws, 0, sizeof(ZSTD_cwksp));705ZSTD_customFree(ptr, customMem);706}707708/**709* Moves the management of a workspace from one cwksp to another. The src cwksp710* is left in an invalid state (src must be re-init()'ed before it's used again).711*/712MEM_STATIC void ZSTD_cwksp_move(ZSTD_cwksp* dst, ZSTD_cwksp* src) {713*dst = *src;714ZSTD_memset(src, 0, sizeof(ZSTD_cwksp));715}716717MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) {718return ws->allocFailed;719}720721/*-*************************************722* Functions Checking Free Space723***************************************/724725/* ZSTD_alignmentSpaceWithinBounds() :726* Returns if the estimated space needed for a wksp is within an acceptable limit of the727* actual amount of space used.728*/729MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp *const ws, size_t const estimatedSpace) {730/* We have an alignment space between objects and tables between tables and buffers, so we can have up to twice731* the alignment bytes difference between estimation and actual usage */732return (estimatedSpace - ZSTD_cwksp_slack_space_required()) <= ZSTD_cwksp_used(ws) &&733ZSTD_cwksp_used(ws) <= estimatedSpace;734}735736737MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws) {738return (size_t)((BYTE*)ws->allocStart - (BYTE*)ws->tableEnd);739}740741MEM_STATIC int ZSTD_cwksp_check_available(ZSTD_cwksp* ws, size_t additionalNeededSpace) {742return ZSTD_cwksp_available_space(ws) >= additionalNeededSpace;743}744745MEM_STATIC int ZSTD_cwksp_check_too_large(ZSTD_cwksp* ws, size_t additionalNeededSpace) {746return ZSTD_cwksp_check_available(747ws, additionalNeededSpace * ZSTD_WORKSPACETOOLARGE_FACTOR);748}749750MEM_STATIC int ZSTD_cwksp_check_wasteful(ZSTD_cwksp* ws, size_t additionalNeededSpace) {751return ZSTD_cwksp_check_too_large(ws, additionalNeededSpace)752&& ws->workspaceOversizedDuration > ZSTD_WORKSPACETOOLARGE_MAXDURATION;753}754755MEM_STATIC void ZSTD_cwksp_bump_oversized_duration(756ZSTD_cwksp* ws, size_t additionalNeededSpace) {757if (ZSTD_cwksp_check_too_large(ws, additionalNeededSpace)) {758ws->workspaceOversizedDuration++;759} else {760ws->workspaceOversizedDuration = 0;761}762}763764#endif /* ZSTD_CWKSP_H */765766767