xref: /freebsd/sbin/hastd/lzf.c (revision f7cee4fa57a2b52a6e43a1f820ce7ba891b8e7d9)
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