11de7b4b8SPedro F. Giffuni /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 48cd3d45aSPawel Jakub Dawidek * Copyright (c) 2000-2008 Marc Alexander Lehmann <schmorp@schmorp.de> 58cd3d45aSPawel Jakub Dawidek * 68cd3d45aSPawel Jakub Dawidek * Redistribution and use in source and binary forms, with or without modifica- 78cd3d45aSPawel Jakub Dawidek * tion, are permitted provided that the following conditions are met: 88cd3d45aSPawel Jakub Dawidek * 98cd3d45aSPawel Jakub Dawidek * 1. Redistributions of source code must retain the above copyright notice, 108cd3d45aSPawel Jakub Dawidek * this list of conditions and the following disclaimer. 118cd3d45aSPawel Jakub Dawidek * 128cd3d45aSPawel Jakub Dawidek * 2. Redistributions in binary form must reproduce the above copyright 138cd3d45aSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer in the 148cd3d45aSPawel Jakub Dawidek * documentation and/or other materials provided with the distribution. 158cd3d45aSPawel Jakub Dawidek * 168cd3d45aSPawel Jakub Dawidek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 178cd3d45aSPawel Jakub Dawidek * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 188cd3d45aSPawel Jakub Dawidek * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 198cd3d45aSPawel Jakub Dawidek * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 208cd3d45aSPawel Jakub Dawidek * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 218cd3d45aSPawel Jakub Dawidek * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 228cd3d45aSPawel Jakub Dawidek * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 238cd3d45aSPawel Jakub Dawidek * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 248cd3d45aSPawel Jakub Dawidek * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 258cd3d45aSPawel Jakub Dawidek * OF THE POSSIBILITY OF SUCH DAMAGE. 268cd3d45aSPawel Jakub Dawidek * 278cd3d45aSPawel Jakub Dawidek * Alternatively, the contents of this file may be used under the terms of 288cd3d45aSPawel Jakub Dawidek * the GNU General Public License ("GPL") version 2 or any later version, 298cd3d45aSPawel Jakub Dawidek * in which case the provisions of the GPL are applicable instead of 308cd3d45aSPawel Jakub Dawidek * the above. If you wish to allow the use of your version of this file 318cd3d45aSPawel Jakub Dawidek * only under the terms of the GPL and not to allow others to use your 328cd3d45aSPawel Jakub Dawidek * version of this file under the BSD license, indicate your decision 338cd3d45aSPawel Jakub Dawidek * by deleting the provisions above and replace them with the notice 348cd3d45aSPawel Jakub Dawidek * and other provisions required by the GPL. If you do not delete the 358cd3d45aSPawel Jakub Dawidek * provisions above, a recipient may use your version of this file under 368cd3d45aSPawel Jakub Dawidek * either the BSD or the GPL. 378cd3d45aSPawel Jakub Dawidek */ 388cd3d45aSPawel Jakub Dawidek 398cd3d45aSPawel Jakub Dawidek #include "lzf.h" 408cd3d45aSPawel Jakub Dawidek 418cd3d45aSPawel Jakub Dawidek #define HSIZE (1 << (HLOG)) 428cd3d45aSPawel Jakub Dawidek 438cd3d45aSPawel Jakub Dawidek /* 448cd3d45aSPawel Jakub Dawidek * don't play with this unless you benchmark! 458cd3d45aSPawel Jakub Dawidek * decompression is not dependent on the hash function 468cd3d45aSPawel Jakub Dawidek * the hashing function might seem strange, just believe me 478cd3d45aSPawel Jakub Dawidek * it works ;) 488cd3d45aSPawel Jakub Dawidek */ 498cd3d45aSPawel Jakub Dawidek #ifndef FRST 508cd3d45aSPawel Jakub Dawidek # define FRST(p) (((p[0]) << 8) | p[1]) 518cd3d45aSPawel Jakub Dawidek # define NEXT(v,p) (((v) << 8) | p[2]) 528cd3d45aSPawel Jakub Dawidek # if ULTRA_FAST 538cd3d45aSPawel Jakub Dawidek # define IDX(h) ((( h >> (3*8 - HLOG)) - h ) & (HSIZE - 1)) 548cd3d45aSPawel Jakub Dawidek # elif VERY_FAST 558cd3d45aSPawel Jakub Dawidek # define IDX(h) ((( h >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) 568cd3d45aSPawel Jakub Dawidek # else 578cd3d45aSPawel Jakub Dawidek # define IDX(h) ((((h ^ (h << 5)) >> (3*8 - HLOG)) - h*5) & (HSIZE - 1)) 588cd3d45aSPawel Jakub Dawidek # endif 598cd3d45aSPawel Jakub Dawidek #endif 608cd3d45aSPawel Jakub Dawidek /* 618cd3d45aSPawel Jakub Dawidek * IDX works because it is very similar to a multiplicative hash, e.g. 628cd3d45aSPawel Jakub Dawidek * ((h * 57321 >> (3*8 - HLOG)) & (HSIZE - 1)) 638cd3d45aSPawel Jakub Dawidek * the latter is also quite fast on newer CPUs, and compresses similarly. 648cd3d45aSPawel Jakub Dawidek * 658cd3d45aSPawel Jakub Dawidek * the next one is also quite good, albeit slow ;) 668cd3d45aSPawel Jakub Dawidek * (int)(cos(h & 0xffffff) * 1e6) 678cd3d45aSPawel Jakub Dawidek */ 688cd3d45aSPawel Jakub Dawidek 698cd3d45aSPawel Jakub Dawidek #if 0 708cd3d45aSPawel Jakub Dawidek /* original lzv-like hash function, much worse and thus slower */ 718cd3d45aSPawel Jakub Dawidek # define FRST(p) (p[0] << 5) ^ p[1] 728cd3d45aSPawel Jakub Dawidek # define NEXT(v,p) ((v) << 5) ^ p[2] 738cd3d45aSPawel Jakub Dawidek # define IDX(h) ((h) & (HSIZE - 1)) 748cd3d45aSPawel Jakub Dawidek #endif 758cd3d45aSPawel Jakub Dawidek 768cd3d45aSPawel Jakub Dawidek #define MAX_LIT (1 << 5) 778cd3d45aSPawel Jakub Dawidek #define MAX_OFF (1 << 13) 788cd3d45aSPawel Jakub Dawidek #define MAX_REF ((1 << 8) + (1 << 3)) 798cd3d45aSPawel Jakub Dawidek 808cd3d45aSPawel Jakub Dawidek #if __GNUC__ >= 3 818cd3d45aSPawel Jakub Dawidek # define expect(expr,value) __builtin_expect ((expr),(value)) 828cd3d45aSPawel Jakub Dawidek # define inline inline 838cd3d45aSPawel Jakub Dawidek #else 848cd3d45aSPawel Jakub Dawidek # define expect(expr,value) (expr) 858cd3d45aSPawel Jakub Dawidek # define inline static 868cd3d45aSPawel Jakub Dawidek #endif 878cd3d45aSPawel Jakub Dawidek 888cd3d45aSPawel Jakub Dawidek #define expect_false(expr) expect ((expr) != 0, 0) 898cd3d45aSPawel Jakub Dawidek #define expect_true(expr) expect ((expr) != 0, 1) 908cd3d45aSPawel Jakub Dawidek 918cd3d45aSPawel Jakub Dawidek /* 928cd3d45aSPawel Jakub Dawidek * compressed format 938cd3d45aSPawel Jakub Dawidek * 948cd3d45aSPawel Jakub Dawidek * 000LLLLL <L+1> ; literal 958cd3d45aSPawel Jakub Dawidek * LLLooooo oooooooo ; backref L 968cd3d45aSPawel Jakub Dawidek * 111ooooo LLLLLLLL oooooooo ; backref L+7 978cd3d45aSPawel Jakub Dawidek * 988cd3d45aSPawel Jakub Dawidek */ 998cd3d45aSPawel Jakub Dawidek 1008cd3d45aSPawel Jakub Dawidek unsigned int 1018cd3d45aSPawel Jakub Dawidek lzf_compress (const void *const in_data, unsigned int in_len, 1028cd3d45aSPawel Jakub Dawidek void *out_data, unsigned int out_len 1038cd3d45aSPawel Jakub Dawidek #if LZF_STATE_ARG 1048cd3d45aSPawel Jakub Dawidek , LZF_STATE htab 1058cd3d45aSPawel Jakub Dawidek #endif 1068cd3d45aSPawel Jakub Dawidek ) 1078cd3d45aSPawel Jakub Dawidek { 1088cd3d45aSPawel Jakub Dawidek #if !LZF_STATE_ARG 1098cd3d45aSPawel Jakub Dawidek LZF_STATE htab; 1108cd3d45aSPawel Jakub Dawidek #endif 1118cd3d45aSPawel Jakub Dawidek const u8 **hslot; 1128cd3d45aSPawel Jakub Dawidek const u8 *ip = (const u8 *)in_data; 1138cd3d45aSPawel Jakub Dawidek u8 *op = (u8 *)out_data; 1148cd3d45aSPawel Jakub Dawidek const u8 *in_end = ip + in_len; 1158cd3d45aSPawel Jakub Dawidek u8 *out_end = op + out_len; 1168cd3d45aSPawel Jakub Dawidek const u8 *ref; 1178cd3d45aSPawel Jakub Dawidek 1188cd3d45aSPawel Jakub Dawidek /* off requires a type wide enough to hold a general pointer difference. 1198cd3d45aSPawel Jakub Dawidek * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only 1208cd3d45aSPawel Jakub Dawidek * works for differences within a single object). We also assume that no 121*f7cee4faSElyes Haouas * bit pattern traps. Since the only platform that is both non-POSIX 1228cd3d45aSPawel Jakub Dawidek * and fails to support both assumptions is windows 64 bit, we make a 1238cd3d45aSPawel Jakub Dawidek * special workaround for it. 1248cd3d45aSPawel Jakub Dawidek */ 1258cd3d45aSPawel Jakub Dawidek #if defined (WIN32) && defined (_M_X64) 1268cd3d45aSPawel Jakub Dawidek unsigned _int64 off; /* workaround for missing POSIX compliance */ 1278cd3d45aSPawel Jakub Dawidek #else 1288cd3d45aSPawel Jakub Dawidek unsigned long off; 1298cd3d45aSPawel Jakub Dawidek #endif 1308cd3d45aSPawel Jakub Dawidek unsigned int hval; 1318cd3d45aSPawel Jakub Dawidek int lit; 1328cd3d45aSPawel Jakub Dawidek 1338cd3d45aSPawel Jakub Dawidek if (!in_len || !out_len) 1348cd3d45aSPawel Jakub Dawidek return 0; 1358cd3d45aSPawel Jakub Dawidek 1368cd3d45aSPawel Jakub Dawidek #if INIT_HTAB 1378cd3d45aSPawel Jakub Dawidek memset (htab, 0, sizeof (htab)); 1388cd3d45aSPawel Jakub Dawidek # if 0 1398cd3d45aSPawel Jakub Dawidek for (hslot = htab; hslot < htab + HSIZE; hslot++) 1408cd3d45aSPawel Jakub Dawidek *hslot++ = ip; 1418cd3d45aSPawel Jakub Dawidek # endif 1428cd3d45aSPawel Jakub Dawidek #endif 1438cd3d45aSPawel Jakub Dawidek 1448cd3d45aSPawel Jakub Dawidek lit = 0; op++; /* start run */ 1458cd3d45aSPawel Jakub Dawidek 1468cd3d45aSPawel Jakub Dawidek hval = FRST (ip); 1478cd3d45aSPawel Jakub Dawidek while (ip < in_end - 2) 1488cd3d45aSPawel Jakub Dawidek { 1498cd3d45aSPawel Jakub Dawidek hval = NEXT (hval, ip); 1508cd3d45aSPawel Jakub Dawidek hslot = htab + IDX (hval); 1518cd3d45aSPawel Jakub Dawidek ref = *hslot; *hslot = ip; 1528cd3d45aSPawel Jakub Dawidek 1538cd3d45aSPawel Jakub Dawidek if (1 1548cd3d45aSPawel Jakub Dawidek #if INIT_HTAB 1558cd3d45aSPawel Jakub Dawidek && ref < ip /* the next test will actually take care of this, but this is faster */ 1568cd3d45aSPawel Jakub Dawidek #endif 1578cd3d45aSPawel Jakub Dawidek && (off = ip - ref - 1) < MAX_OFF 1588cd3d45aSPawel Jakub Dawidek && ip + 4 < in_end 1598cd3d45aSPawel Jakub Dawidek && ref > (const u8 *)in_data 1608cd3d45aSPawel Jakub Dawidek #if STRICT_ALIGN 1618cd3d45aSPawel Jakub Dawidek && ref[0] == ip[0] 1628cd3d45aSPawel Jakub Dawidek && ref[1] == ip[1] 1638cd3d45aSPawel Jakub Dawidek && ref[2] == ip[2] 1648cd3d45aSPawel Jakub Dawidek #else 1658cd3d45aSPawel Jakub Dawidek && *(const u16 *)ref == *(const u16 *)ip 1668cd3d45aSPawel Jakub Dawidek && ref[2] == ip[2] 1678cd3d45aSPawel Jakub Dawidek #endif 1688cd3d45aSPawel Jakub Dawidek ) 1698cd3d45aSPawel Jakub Dawidek { 1708cd3d45aSPawel Jakub Dawidek /* match found at *ref++ */ 1718cd3d45aSPawel Jakub Dawidek unsigned int len = 2; 1728cd3d45aSPawel Jakub Dawidek unsigned int maxlen = in_end - ip - len; 1738cd3d45aSPawel Jakub Dawidek maxlen = maxlen > MAX_REF ? MAX_REF : maxlen; 1748cd3d45aSPawel Jakub Dawidek 1758cd3d45aSPawel Jakub Dawidek if (expect_false (op + 3 + 1 >= out_end)) /* first a faster conservative test */ 1768cd3d45aSPawel Jakub Dawidek if (op - !lit + 3 + 1 >= out_end) /* second the exact but rare test */ 1778cd3d45aSPawel Jakub Dawidek return 0; 1788cd3d45aSPawel Jakub Dawidek 1798cd3d45aSPawel Jakub Dawidek op [- lit - 1] = lit - 1; /* stop run */ 1808cd3d45aSPawel Jakub Dawidek op -= !lit; /* undo run if length is zero */ 1818cd3d45aSPawel Jakub Dawidek 1828cd3d45aSPawel Jakub Dawidek for (;;) 1838cd3d45aSPawel Jakub Dawidek { 1848cd3d45aSPawel Jakub Dawidek if (expect_true (maxlen > 16)) 1858cd3d45aSPawel Jakub Dawidek { 1868cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1878cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1888cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1898cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1908cd3d45aSPawel Jakub Dawidek 1918cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1928cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1938cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1948cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1958cd3d45aSPawel Jakub Dawidek 1968cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1978cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1988cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 1998cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 2008cd3d45aSPawel Jakub Dawidek 2018cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 2028cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 2038cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 2048cd3d45aSPawel Jakub Dawidek len++; if (ref [len] != ip [len]) break; 2058cd3d45aSPawel Jakub Dawidek } 2068cd3d45aSPawel Jakub Dawidek 2078cd3d45aSPawel Jakub Dawidek do 2088cd3d45aSPawel Jakub Dawidek len++; 2098cd3d45aSPawel Jakub Dawidek while (len < maxlen && ref[len] == ip[len]); 2108cd3d45aSPawel Jakub Dawidek 2118cd3d45aSPawel Jakub Dawidek break; 2128cd3d45aSPawel Jakub Dawidek } 2138cd3d45aSPawel Jakub Dawidek 2148cd3d45aSPawel Jakub Dawidek len -= 2; /* len is now #octets - 1 */ 2158cd3d45aSPawel Jakub Dawidek ip++; 2168cd3d45aSPawel Jakub Dawidek 2178cd3d45aSPawel Jakub Dawidek if (len < 7) 2188cd3d45aSPawel Jakub Dawidek { 2198cd3d45aSPawel Jakub Dawidek *op++ = (off >> 8) + (len << 5); 2208cd3d45aSPawel Jakub Dawidek } 2218cd3d45aSPawel Jakub Dawidek else 2228cd3d45aSPawel Jakub Dawidek { 2238cd3d45aSPawel Jakub Dawidek *op++ = (off >> 8) + ( 7 << 5); 2248cd3d45aSPawel Jakub Dawidek *op++ = len - 7; 2258cd3d45aSPawel Jakub Dawidek } 2268cd3d45aSPawel Jakub Dawidek 2278cd3d45aSPawel Jakub Dawidek *op++ = off; 2288cd3d45aSPawel Jakub Dawidek lit = 0; op++; /* start run */ 2298cd3d45aSPawel Jakub Dawidek 2308cd3d45aSPawel Jakub Dawidek ip += len + 1; 2318cd3d45aSPawel Jakub Dawidek 2328cd3d45aSPawel Jakub Dawidek if (expect_false (ip >= in_end - 2)) 2338cd3d45aSPawel Jakub Dawidek break; 2348cd3d45aSPawel Jakub Dawidek 2358cd3d45aSPawel Jakub Dawidek #if ULTRA_FAST || VERY_FAST 2368cd3d45aSPawel Jakub Dawidek --ip; 2378cd3d45aSPawel Jakub Dawidek # if VERY_FAST && !ULTRA_FAST 2388cd3d45aSPawel Jakub Dawidek --ip; 2398cd3d45aSPawel Jakub Dawidek # endif 2408cd3d45aSPawel Jakub Dawidek hval = FRST (ip); 2418cd3d45aSPawel Jakub Dawidek 2428cd3d45aSPawel Jakub Dawidek hval = NEXT (hval, ip); 2438cd3d45aSPawel Jakub Dawidek htab[IDX (hval)] = ip; 2448cd3d45aSPawel Jakub Dawidek ip++; 2458cd3d45aSPawel Jakub Dawidek 2468cd3d45aSPawel Jakub Dawidek # if VERY_FAST && !ULTRA_FAST 2478cd3d45aSPawel Jakub Dawidek hval = NEXT (hval, ip); 2488cd3d45aSPawel Jakub Dawidek htab[IDX (hval)] = ip; 2498cd3d45aSPawel Jakub Dawidek ip++; 2508cd3d45aSPawel Jakub Dawidek # endif 2518cd3d45aSPawel Jakub Dawidek #else 2528cd3d45aSPawel Jakub Dawidek ip -= len + 1; 2538cd3d45aSPawel Jakub Dawidek 2548cd3d45aSPawel Jakub Dawidek do 2558cd3d45aSPawel Jakub Dawidek { 2568cd3d45aSPawel Jakub Dawidek hval = NEXT (hval, ip); 2578cd3d45aSPawel Jakub Dawidek htab[IDX (hval)] = ip; 2588cd3d45aSPawel Jakub Dawidek ip++; 2598cd3d45aSPawel Jakub Dawidek } 2608cd3d45aSPawel Jakub Dawidek while (len--); 2618cd3d45aSPawel Jakub Dawidek #endif 2628cd3d45aSPawel Jakub Dawidek } 2638cd3d45aSPawel Jakub Dawidek else 2648cd3d45aSPawel Jakub Dawidek { 2658cd3d45aSPawel Jakub Dawidek /* one more literal byte we must copy */ 2668cd3d45aSPawel Jakub Dawidek if (expect_false (op >= out_end)) 2678cd3d45aSPawel Jakub Dawidek return 0; 2688cd3d45aSPawel Jakub Dawidek 2698cd3d45aSPawel Jakub Dawidek lit++; *op++ = *ip++; 2708cd3d45aSPawel Jakub Dawidek 2718cd3d45aSPawel Jakub Dawidek if (expect_false (lit == MAX_LIT)) 2728cd3d45aSPawel Jakub Dawidek { 2738cd3d45aSPawel Jakub Dawidek op [- lit - 1] = lit - 1; /* stop run */ 2748cd3d45aSPawel Jakub Dawidek lit = 0; op++; /* start run */ 2758cd3d45aSPawel Jakub Dawidek } 2768cd3d45aSPawel Jakub Dawidek } 2778cd3d45aSPawel Jakub Dawidek } 2788cd3d45aSPawel Jakub Dawidek 2798cd3d45aSPawel Jakub Dawidek if (op + 3 > out_end) /* at most 3 bytes can be missing here */ 2808cd3d45aSPawel Jakub Dawidek return 0; 2818cd3d45aSPawel Jakub Dawidek 2828cd3d45aSPawel Jakub Dawidek while (ip < in_end) 2838cd3d45aSPawel Jakub Dawidek { 2848cd3d45aSPawel Jakub Dawidek lit++; *op++ = *ip++; 2858cd3d45aSPawel Jakub Dawidek 2868cd3d45aSPawel Jakub Dawidek if (expect_false (lit == MAX_LIT)) 2878cd3d45aSPawel Jakub Dawidek { 2888cd3d45aSPawel Jakub Dawidek op [- lit - 1] = lit - 1; /* stop run */ 2898cd3d45aSPawel Jakub Dawidek lit = 0; op++; /* start run */ 2908cd3d45aSPawel Jakub Dawidek } 2918cd3d45aSPawel Jakub Dawidek } 2928cd3d45aSPawel Jakub Dawidek 2938cd3d45aSPawel Jakub Dawidek op [- lit - 1] = lit - 1; /* end run */ 2948cd3d45aSPawel Jakub Dawidek op -= !lit; /* undo run if length is zero */ 2958cd3d45aSPawel Jakub Dawidek 2968cd3d45aSPawel Jakub Dawidek return op - (u8 *)out_data; 2978cd3d45aSPawel Jakub Dawidek } 2988cd3d45aSPawel Jakub Dawidek 2998cd3d45aSPawel Jakub Dawidek #if AVOID_ERRNO 3008cd3d45aSPawel Jakub Dawidek # define SET_ERRNO(n) 3018cd3d45aSPawel Jakub Dawidek #else 3028cd3d45aSPawel Jakub Dawidek # include <errno.h> 3038cd3d45aSPawel Jakub Dawidek # define SET_ERRNO(n) errno = (n) 3048cd3d45aSPawel Jakub Dawidek #endif 3058cd3d45aSPawel Jakub Dawidek 3068cd3d45aSPawel Jakub Dawidek #if (__i386 || __amd64) && __GNUC__ >= 3 3078cd3d45aSPawel Jakub Dawidek # define lzf_movsb(dst, src, len) \ 3088cd3d45aSPawel Jakub Dawidek asm ("rep movsb" \ 3098cd3d45aSPawel Jakub Dawidek : "=D" (dst), "=S" (src), "=c" (len) \ 3108cd3d45aSPawel Jakub Dawidek : "0" (dst), "1" (src), "2" (len)); 3118cd3d45aSPawel Jakub Dawidek #endif 3128cd3d45aSPawel Jakub Dawidek 3138cd3d45aSPawel Jakub Dawidek unsigned int 3148cd3d45aSPawel Jakub Dawidek lzf_decompress (const void *const in_data, unsigned int in_len, 3158cd3d45aSPawel Jakub Dawidek void *out_data, unsigned int out_len) 3168cd3d45aSPawel Jakub Dawidek { 3178cd3d45aSPawel Jakub Dawidek u8 const *ip = (const u8 *)in_data; 3188cd3d45aSPawel Jakub Dawidek u8 *op = (u8 *)out_data; 3198cd3d45aSPawel Jakub Dawidek u8 const *const in_end = ip + in_len; 3208cd3d45aSPawel Jakub Dawidek u8 *const out_end = op + out_len; 3218cd3d45aSPawel Jakub Dawidek 3228cd3d45aSPawel Jakub Dawidek do 3238cd3d45aSPawel Jakub Dawidek { 3248cd3d45aSPawel Jakub Dawidek unsigned int ctrl = *ip++; 3258cd3d45aSPawel Jakub Dawidek 3268cd3d45aSPawel Jakub Dawidek if (ctrl < (1 << 5)) /* literal run */ 3278cd3d45aSPawel Jakub Dawidek { 3288cd3d45aSPawel Jakub Dawidek ctrl++; 3298cd3d45aSPawel Jakub Dawidek 3308cd3d45aSPawel Jakub Dawidek if (op + ctrl > out_end) 3318cd3d45aSPawel Jakub Dawidek { 3328cd3d45aSPawel Jakub Dawidek SET_ERRNO (E2BIG); 3338cd3d45aSPawel Jakub Dawidek return 0; 3348cd3d45aSPawel Jakub Dawidek } 3358cd3d45aSPawel Jakub Dawidek 3368cd3d45aSPawel Jakub Dawidek #if CHECK_INPUT 3378cd3d45aSPawel Jakub Dawidek if (ip + ctrl > in_end) 3388cd3d45aSPawel Jakub Dawidek { 3398cd3d45aSPawel Jakub Dawidek SET_ERRNO (EINVAL); 3408cd3d45aSPawel Jakub Dawidek return 0; 3418cd3d45aSPawel Jakub Dawidek } 3428cd3d45aSPawel Jakub Dawidek #endif 3438cd3d45aSPawel Jakub Dawidek 3448cd3d45aSPawel Jakub Dawidek #ifdef lzf_movsb 3458cd3d45aSPawel Jakub Dawidek lzf_movsb (op, ip, ctrl); 3468cd3d45aSPawel Jakub Dawidek #else 3478cd3d45aSPawel Jakub Dawidek do 3488cd3d45aSPawel Jakub Dawidek *op++ = *ip++; 3498cd3d45aSPawel Jakub Dawidek while (--ctrl); 3508cd3d45aSPawel Jakub Dawidek #endif 3518cd3d45aSPawel Jakub Dawidek } 3528cd3d45aSPawel Jakub Dawidek else /* back reference */ 3538cd3d45aSPawel Jakub Dawidek { 3548cd3d45aSPawel Jakub Dawidek unsigned int len = ctrl >> 5; 3558cd3d45aSPawel Jakub Dawidek 3568cd3d45aSPawel Jakub Dawidek u8 *ref = op - ((ctrl & 0x1f) << 8) - 1; 3578cd3d45aSPawel Jakub Dawidek 3588cd3d45aSPawel Jakub Dawidek #if CHECK_INPUT 3598cd3d45aSPawel Jakub Dawidek if (ip >= in_end) 3608cd3d45aSPawel Jakub Dawidek { 3618cd3d45aSPawel Jakub Dawidek SET_ERRNO (EINVAL); 3628cd3d45aSPawel Jakub Dawidek return 0; 3638cd3d45aSPawel Jakub Dawidek } 3648cd3d45aSPawel Jakub Dawidek #endif 3658cd3d45aSPawel Jakub Dawidek if (len == 7) 3668cd3d45aSPawel Jakub Dawidek { 3678cd3d45aSPawel Jakub Dawidek len += *ip++; 3688cd3d45aSPawel Jakub Dawidek #if CHECK_INPUT 3698cd3d45aSPawel Jakub Dawidek if (ip >= in_end) 3708cd3d45aSPawel Jakub Dawidek { 3718cd3d45aSPawel Jakub Dawidek SET_ERRNO (EINVAL); 3728cd3d45aSPawel Jakub Dawidek return 0; 3738cd3d45aSPawel Jakub Dawidek } 3748cd3d45aSPawel Jakub Dawidek #endif 3758cd3d45aSPawel Jakub Dawidek } 3768cd3d45aSPawel Jakub Dawidek 3778cd3d45aSPawel Jakub Dawidek ref -= *ip++; 3788cd3d45aSPawel Jakub Dawidek 3798cd3d45aSPawel Jakub Dawidek if (op + len + 2 > out_end) 3808cd3d45aSPawel Jakub Dawidek { 3818cd3d45aSPawel Jakub Dawidek SET_ERRNO (E2BIG); 3828cd3d45aSPawel Jakub Dawidek return 0; 3838cd3d45aSPawel Jakub Dawidek } 3848cd3d45aSPawel Jakub Dawidek 3858cd3d45aSPawel Jakub Dawidek if (ref < (u8 *)out_data) 3868cd3d45aSPawel Jakub Dawidek { 3878cd3d45aSPawel Jakub Dawidek SET_ERRNO (EINVAL); 3888cd3d45aSPawel Jakub Dawidek return 0; 3898cd3d45aSPawel Jakub Dawidek } 3908cd3d45aSPawel Jakub Dawidek 3918cd3d45aSPawel Jakub Dawidek #ifdef lzf_movsb 3928cd3d45aSPawel Jakub Dawidek len += 2; 3938cd3d45aSPawel Jakub Dawidek lzf_movsb (op, ref, len); 3948cd3d45aSPawel Jakub Dawidek #else 3958cd3d45aSPawel Jakub Dawidek *op++ = *ref++; 3968cd3d45aSPawel Jakub Dawidek *op++ = *ref++; 3978cd3d45aSPawel Jakub Dawidek 3988cd3d45aSPawel Jakub Dawidek do 3998cd3d45aSPawel Jakub Dawidek *op++ = *ref++; 4008cd3d45aSPawel Jakub Dawidek while (--len); 4018cd3d45aSPawel Jakub Dawidek #endif 4028cd3d45aSPawel Jakub Dawidek } 4038cd3d45aSPawel Jakub Dawidek } 4048cd3d45aSPawel Jakub Dawidek while (ip < in_end); 4058cd3d45aSPawel Jakub Dawidek 4068cd3d45aSPawel Jakub Dawidek return op - (u8 *)out_data; 4078cd3d45aSPawel Jakub Dawidek } 4088cd3d45aSPawel Jakub Dawidek 409