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 }