kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

lz4frame.c (5661B)


      1 #include "lz4frame.h"
      2 
      3 #include <stddef.h>
      4 #include <string.h>
      5 
      6 /* --- Amalgamated upstream LZ4 frame layer ---------------------------------
      7  * The block codec (vendor/lz4/lz4.c) is compiled as its own TU (src/dist/lz4.c)
      8  * with default external linkage, so here we pull in only the frame, high-
      9  * compression, and xxhash sources. lz4hc.c re-includes lz4.c with
     10  * LZ4_COMMONDEFS_ONLY for the static common helpers (LZ4_count, mem, etc.), so
     11  * those stay TU-local and the public LZ4_* block symbols resolve against the
     12  * block TU at link time -- no duplicate-definition clash. Include order
     13  * matters: lz4hc.c (-> lz4.c) sets LZ4_SRC_INCLUDED before lz4frame.c, so the
     14  * frame sources share lz4.c's definitions rather than re-declaring them. */
     15 #define LZ4LIB_VISIBILITY
     16 #define LZ4FLIB_VISIBILITY
     17 #define LZ4F_STATIC_LINKING_ONLY
     18 #define XXH_STATIC_LINKING_ONLY
     19 
     20 /* The frame/HC sources retain a default (non-custom) allocator path. It is dead
     21  * code here -- every context is created with a KitHeap-backed LZ4F_CustomMem
     22  * (see lz4f_cmem) -- but it is still compiled. Route it through LZ4's supported
     23  * LZ4_USER_MEMORY_FUNCTIONS hook to trivial stubs so libkit pulls in no libc
     24  * malloc/free, matching how lz4.c pins the block allocator. If ever reached,
     25  * the stubs fail allocation safely (NULL), which LZ4F surfaces as an error. */
     26 #define LZ4_USER_MEMORY_FUNCTIONS
     27 void* LZ4_malloc(size_t s) {
     28   (void)s;
     29   return NULL;
     30 }
     31 void* LZ4_calloc(size_t n, size_t s) {
     32   (void)n;
     33   (void)s;
     34   return NULL;
     35 }
     36 void LZ4_free(void* p) { (void)p; }
     37 
     38 // clang-format off
     39 #include "lz4/lz4hc.c"
     40 #include "lz4/xxhash.c"
     41 #include "lz4/lz4frame.c"
     42 // clang-format on
     43 
     44 /* KitHeap->free needs the original allocation size, but LZ4F_FreeFunction
     45  * passes only the pointer. Stash the size in an aligned header just before the
     46  * returned block. LZ4F_MAX_ALIGN (16) both fits a size_t and is >= the
     47  * alignment of max_align_t on supported targets, so the returned pointer stays
     48  * suitably aligned. */
     49 #define LZ4F_MAX_ALIGN 16u
     50 
     51 /* Output buffer for a single streaming decompress pass. */
     52 #define LZ4F_DEC_CHUNK (128u * 1024u)
     53 
     54 static void* lz4f_heap_alloc(void* opaque, size_t size) {
     55   KitHeap* heap = (KitHeap*)opaque;
     56   size_t total = size + LZ4F_MAX_ALIGN;
     57   unsigned char* raw;
     58   if (total < size) return NULL; /* overflow */
     59   raw = (unsigned char*)heap->alloc(heap, total, LZ4F_MAX_ALIGN);
     60   if (!raw) return NULL;
     61   memcpy(raw, &total, sizeof total);
     62   return raw + LZ4F_MAX_ALIGN;
     63 }
     64 
     65 static void lz4f_heap_free(void* opaque, void* p) {
     66   KitHeap* heap = (KitHeap*)opaque;
     67   unsigned char* raw;
     68   size_t total;
     69   if (!p) return;
     70   raw = (unsigned char*)p - LZ4F_MAX_ALIGN;
     71   memcpy(&total, raw, sizeof total);
     72   heap->free(heap, raw, total);
     73 }
     74 
     75 static LZ4F_CustomMem lz4f_cmem(KitHeap* heap) {
     76   LZ4F_CustomMem cmem;
     77   cmem.customAlloc = lz4f_heap_alloc;
     78   cmem.customCalloc = NULL; /* lz4frame falls back to customAlloc + memset */
     79   cmem.customFree = lz4f_heap_free;
     80   cmem.opaqueState = heap;
     81   return cmem;
     82 }
     83 
     84 int dist_lz4f_compress(KitHeap* heap, KitWriter* out, const uint8_t* data,
     85                        size_t len) {
     86   LZ4F_CustomMem cmem;
     87   LZ4F_cctx* cctx;
     88   LZ4F_preferences_t prefs;
     89   unsigned char* buf = NULL;
     90   size_t cap, off = 0, n;
     91   int rc = DIST_ERR;
     92 
     93   if (!heap || !out || (!data && len != 0)) return DIST_ERR;
     94 
     95   cmem = lz4f_cmem(heap);
     96   cctx = LZ4F_createCompressionContext_advanced(cmem, LZ4F_VERSION);
     97   if (!cctx) return DIST_ERR;
     98 
     99   memset(&prefs, 0, sizeof prefs);
    100   /* Record the original size in the frame header (so `lz4 --list` reports it
    101    * and decode can validate). compressionLevel 0 = fast default; no extra
    102    * checksums, matching the `lz4` CLI default. */
    103   prefs.frameInfo.contentSize = (unsigned long long)len;
    104 
    105   cap = (size_t)LZ4F_HEADER_SIZE_MAX + LZ4F_compressBound(len, &prefs) +
    106         LZ4F_compressBound(0, &prefs);
    107   buf = (unsigned char*)heap->alloc(heap, cap, LZ4F_MAX_ALIGN);
    108   if (!buf) goto done;
    109 
    110   n = LZ4F_compressBegin(cctx, buf + off, cap - off, &prefs);
    111   if (LZ4F_isError(n)) goto done;
    112   off += n;
    113 
    114   if (len > 0) {
    115     n = LZ4F_compressUpdate(cctx, buf + off, cap - off, data, len, NULL);
    116     if (LZ4F_isError(n)) goto done;
    117     off += n;
    118   }
    119 
    120   n = LZ4F_compressEnd(cctx, buf + off, cap - off, NULL);
    121   if (LZ4F_isError(n)) goto done;
    122   off += n;
    123 
    124   if (kit_writer_write(out, buf, off) != KIT_OK) goto done;
    125   rc = DIST_OK;
    126 
    127 done:
    128   if (buf) heap->free(heap, buf, cap);
    129   LZ4F_freeCompressionContext(cctx);
    130   return rc;
    131 }
    132 
    133 int dist_lz4f_decompress(KitHeap* heap, KitWriter* out, const uint8_t* data,
    134                          size_t len) {
    135   LZ4F_CustomMem cmem;
    136   LZ4F_dctx* dctx;
    137   unsigned char* obuf = NULL;
    138   const unsigned char* src = data;
    139   size_t remaining = len;
    140   int rc = DIST_ERR;
    141 
    142   if (!heap || !out || (!data && len != 0)) return DIST_ERR;
    143 
    144   cmem = lz4f_cmem(heap);
    145   dctx = LZ4F_createDecompressionContext_advanced(cmem, LZ4F_VERSION);
    146   if (!dctx) return DIST_ERR;
    147 
    148   obuf = (unsigned char*)heap->alloc(heap, LZ4F_DEC_CHUNK, LZ4F_MAX_ALIGN);
    149   if (!obuf) goto done;
    150 
    151   for (;;) {
    152     size_t dst_sz = LZ4F_DEC_CHUNK;
    153     size_t src_sz = remaining;
    154     size_t ret = LZ4F_decompress(dctx, obuf, &dst_sz, src, &src_sz, NULL);
    155     if (LZ4F_isError(ret)) goto done;
    156     if (dst_sz > 0 && kit_writer_write(out, obuf, dst_sz) != KIT_OK) goto done;
    157     src += src_sz;
    158     remaining -= src_sz;
    159     if (ret == 0) break;                       /* frame fully decoded */
    160     if (src_sz == 0 && dst_sz == 0) goto done; /* no progress => truncated */
    161   }
    162   rc = DIST_OK;
    163 
    164 done:
    165   if (obuf) heap->free(heap, obuf, LZ4F_DEC_CHUNK);
    166   LZ4F_freeDecompressionContext(dctx);
    167   return rc;
    168 }