2007-07-11 04:22:24 +04:00
/*
2012-08-13 19:25:44 +04:00
* LZO1X Compressor from LZO
2007-07-11 04:22:24 +04:00
*
2012-08-13 19:25:44 +04:00
* Copyright ( C ) 1996 - 2012 Markus F . X . J . Oberhumer < markus @ oberhumer . com >
2007-07-11 04:22:24 +04:00
*
* The full LZO package can be found at :
* http : //www.oberhumer.com/opensource/lzo/
*
2012-08-13 19:25:44 +04:00
* Changed for Linux kernel use by :
2007-07-11 04:22:24 +04:00
* Nitin Gupta < nitingupta910 @ gmail . com >
* Richard Purdie < rpurdie @ openedhand . com >
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <asm/unaligned.h>
2012-08-13 19:25:44 +04:00
# include <linux/lzo.h>
2007-07-11 04:22:24 +04:00
# include "lzodefs.h"
static noinline size_t
2012-08-13 19:25:44 +04:00
lzo1x_1_do_compress ( const unsigned char * in , size_t in_len ,
unsigned char * out , size_t * out_len ,
size_t ti , void * wrkmem )
2007-07-11 04:22:24 +04:00
{
2012-08-13 19:25:44 +04:00
const unsigned char * ip ;
unsigned char * op ;
2007-07-11 04:22:24 +04:00
const unsigned char * const in_end = in + in_len ;
2012-08-13 19:25:44 +04:00
const unsigned char * const ip_end = in + in_len - 20 ;
const unsigned char * ii ;
lzo_dict_t * const dict = ( lzo_dict_t * ) wrkmem ;
2007-07-11 04:22:24 +04:00
2012-08-13 19:25:44 +04:00
op = out ;
ip = in ;
ii = ip ;
ip + = ti < 4 ? 4 - ti : 0 ;
2007-07-11 04:22:24 +04:00
for ( ; ; ) {
2012-08-13 19:25:44 +04:00
const unsigned char * m_pos ;
size_t t , m_len , m_off ;
u32 dv ;
2007-07-11 04:22:24 +04:00
literal :
2012-08-13 19:25:44 +04:00
ip + = 1 + ( ( ip - ii ) > > 5 ) ;
next :
2007-07-11 04:22:24 +04:00
if ( unlikely ( ip > = ip_end ) )
break ;
2012-08-13 19:25:44 +04:00
dv = get_unaligned_le32 ( ip ) ;
t = ( ( dv * 0x1824429d ) > > ( 32 - D_BITS ) ) & D_MASK ;
m_pos = in + dict [ t ] ;
dict [ t ] = ( lzo_dict_t ) ( ip - in ) ;
if ( unlikely ( dv ! = get_unaligned_le32 ( m_pos ) ) )
goto literal ;
2007-07-11 04:22:24 +04:00
2012-08-13 19:25:44 +04:00
ii - = ti ;
ti = 0 ;
t = ip - ii ;
if ( t ! = 0 ) {
2007-07-11 04:22:24 +04:00
if ( t < = 3 ) {
op [ - 2 ] | = t ;
2012-08-13 19:25:44 +04:00
COPY4 ( op , ii ) ;
op + = t ;
} else if ( t < = 16 ) {
2007-07-11 04:22:24 +04:00
* op + + = ( t - 3 ) ;
2012-08-13 19:25:44 +04:00
COPY8 ( op , ii ) ;
COPY8 ( op + 8 , ii + 8 ) ;
op + = t ;
2007-07-11 04:22:24 +04:00
} else {
2012-08-13 19:25:44 +04:00
if ( t < = 18 ) {
* op + + = ( t - 3 ) ;
} else {
size_t tt = t - 18 ;
2007-07-11 04:22:24 +04:00
* op + + = 0 ;
2012-08-13 19:25:44 +04:00
while ( unlikely ( tt > 255 ) ) {
tt - = 255 ;
* op + + = 0 ;
}
* op + + = tt ;
2007-07-11 04:22:24 +04:00
}
2012-08-13 19:25:44 +04:00
do {
COPY8 ( op , ii ) ;
COPY8 ( op + 8 , ii + 8 ) ;
op + = 16 ;
ii + = 16 ;
t - = 16 ;
} while ( t > = 16 ) ;
if ( t > 0 ) do {
* op + + = * ii + + ;
} while ( - - t > 0 ) ;
2007-07-11 04:22:24 +04:00
}
}
2012-08-13 19:25:44 +04:00
m_len = 4 ;
{
# if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ64)
u64 v ;
v = get_unaligned ( ( const u64 * ) ( ip + m_len ) ) ^
get_unaligned ( ( const u64 * ) ( m_pos + m_len ) ) ;
if ( unlikely ( v = = 0 ) ) {
do {
m_len + = 8 ;
v = get_unaligned ( ( const u64 * ) ( ip + m_len ) ) ^
get_unaligned ( ( const u64 * ) ( m_pos + m_len ) ) ;
if ( unlikely ( ip + m_len > = ip_end ) )
goto m_len_done ;
} while ( v = = 0 ) ;
}
# if defined(__LITTLE_ENDIAN)
m_len + = ( unsigned ) __builtin_ctzll ( v ) / 8 ;
# elif defined(__BIG_ENDIAN)
m_len + = ( unsigned ) __builtin_clzll ( v ) / 8 ;
# else
# error "missing endian definition"
# endif
# elif defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ32)
u32 v ;
v = get_unaligned ( ( const u32 * ) ( ip + m_len ) ) ^
get_unaligned ( ( const u32 * ) ( m_pos + m_len ) ) ;
if ( unlikely ( v = = 0 ) ) {
do {
m_len + = 4 ;
v = get_unaligned ( ( const u32 * ) ( ip + m_len ) ) ^
get_unaligned ( ( const u32 * ) ( m_pos + m_len ) ) ;
if ( v ! = 0 )
break ;
m_len + = 4 ;
v = get_unaligned ( ( const u32 * ) ( ip + m_len ) ) ^
get_unaligned ( ( const u32 * ) ( m_pos + m_len ) ) ;
if ( unlikely ( ip + m_len > = ip_end ) )
goto m_len_done ;
} while ( v = = 0 ) ;
}
# if defined(__LITTLE_ENDIAN)
m_len + = ( unsigned ) __builtin_ctz ( v ) / 8 ;
# elif defined(__BIG_ENDIAN)
m_len + = ( unsigned ) __builtin_clz ( v ) / 8 ;
# else
# error "missing endian definition"
# endif
# else
if ( unlikely ( ip [ m_len ] = = m_pos [ m_len ] ) ) {
do {
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( ip [ m_len ] ! = m_pos [ m_len ] )
break ;
m_len + = 1 ;
if ( unlikely ( ip + m_len > = ip_end ) )
goto m_len_done ;
} while ( ip [ m_len ] = = m_pos [ m_len ] ) ;
}
# endif
}
m_len_done :
2007-07-11 04:22:24 +04:00
2012-08-13 19:25:44 +04:00
m_off = ip - m_pos ;
ip + = m_len ;
ii = ip ;
if ( m_len < = M2_MAX_LEN & & m_off < = M2_MAX_OFFSET ) {
m_off - = 1 ;
* op + + = ( ( ( m_len - 1 ) < < 5 ) | ( ( m_off & 7 ) < < 2 ) ) ;
* op + + = ( m_off > > 3 ) ;
} else if ( m_off < = M3_MAX_OFFSET ) {
m_off - = 1 ;
if ( m_len < = M3_MAX_LEN )
2007-07-11 04:22:24 +04:00
* op + + = ( M3_MARKER | ( m_len - 2 ) ) ;
2012-08-13 19:25:44 +04:00
else {
m_len - = M3_MAX_LEN ;
* op + + = M3_MARKER | 0 ;
while ( unlikely ( m_len > 255 ) ) {
m_len - = 255 ;
* op + + = 0 ;
}
* op + + = ( m_len ) ;
2007-07-11 04:22:24 +04:00
}
2012-08-13 19:25:44 +04:00
* op + + = ( m_off < < 2 ) ;
* op + + = ( m_off > > 6 ) ;
2007-07-11 04:22:24 +04:00
} else {
2012-08-13 19:25:44 +04:00
m_off - = 0x4000 ;
if ( m_len < = M4_MAX_LEN )
* op + + = ( M4_MARKER | ( ( m_off > > 11 ) & 8 )
2007-07-11 04:22:24 +04:00
| ( m_len - 2 ) ) ;
2012-08-13 19:25:44 +04:00
else {
m_len - = M4_MAX_LEN ;
* op + + = ( M4_MARKER | ( ( m_off > > 11 ) & 8 ) ) ;
while ( unlikely ( m_len > 255 ) ) {
m_len - = 255 ;
* op + + = 0 ;
2007-07-11 04:22:24 +04:00
}
2012-08-13 19:25:44 +04:00
* op + + = ( m_len ) ;
2007-07-11 04:22:24 +04:00
}
2012-08-13 19:25:44 +04:00
* op + + = ( m_off < < 2 ) ;
2007-07-11 04:22:24 +04:00
* op + + = ( m_off > > 6 ) ;
}
2012-08-13 19:25:44 +04:00
goto next ;
2007-07-11 04:22:24 +04:00
}
* out_len = op - out ;
2012-08-13 19:25:44 +04:00
return in_end - ( ii - ti ) ;
2007-07-11 04:22:24 +04:00
}
2012-08-13 19:25:44 +04:00
int lzo1x_1_compress ( const unsigned char * in , size_t in_len ,
unsigned char * out , size_t * out_len ,
void * wrkmem )
2007-07-11 04:22:24 +04:00
{
2012-08-13 19:25:44 +04:00
const unsigned char * ip = in ;
2007-07-11 04:22:24 +04:00
unsigned char * op = out ;
2012-08-13 19:25:44 +04:00
size_t l = in_len ;
size_t t = 0 ;
2007-07-11 04:22:24 +04:00
2012-08-13 19:25:44 +04:00
while ( l > 20 ) {
size_t ll = l < = ( M4_MAX_OFFSET + 1 ) ? l : ( M4_MAX_OFFSET + 1 ) ;
uintptr_t ll_end = ( uintptr_t ) ip + ll ;
if ( ( ll_end + ( ( t + ll ) > > 5 ) ) < = ll_end )
break ;
BUILD_BUG_ON ( D_SIZE * sizeof ( lzo_dict_t ) > LZO1X_1_MEM_COMPRESS ) ;
memset ( wrkmem , 0 , D_SIZE * sizeof ( lzo_dict_t ) ) ;
t = lzo1x_1_do_compress ( ip , ll , op , out_len , t , wrkmem ) ;
ip + = ll ;
2007-07-11 04:22:24 +04:00
op + = * out_len ;
2012-08-13 19:25:44 +04:00
l - = ll ;
2007-07-11 04:22:24 +04:00
}
2012-08-13 19:25:44 +04:00
t + = l ;
2007-07-11 04:22:24 +04:00
if ( t > 0 ) {
2012-08-13 19:25:44 +04:00
const unsigned char * ii = in + in_len - t ;
2007-07-11 04:22:24 +04:00
if ( op = = out & & t < = 238 ) {
* op + + = ( 17 + t ) ;
} else if ( t < = 3 ) {
op [ - 2 ] | = t ;
} else if ( t < = 18 ) {
* op + + = ( t - 3 ) ;
} else {
size_t tt = t - 18 ;
* op + + = 0 ;
while ( tt > 255 ) {
tt - = 255 ;
* op + + = 0 ;
}
* op + + = tt ;
}
2012-08-13 19:25:44 +04:00
if ( t > = 16 ) do {
COPY8 ( op , ii ) ;
COPY8 ( op + 8 , ii + 8 ) ;
op + = 16 ;
ii + = 16 ;
t - = 16 ;
} while ( t > = 16 ) ;
if ( t > 0 ) do {
2007-07-11 04:22:24 +04:00
* op + + = * ii + + ;
} while ( - - t > 0 ) ;
}
* op + + = M4_MARKER | 1 ;
* op + + = 0 ;
* op + + = 0 ;
* out_len = op - out ;
return LZO_E_OK ;
}
EXPORT_SYMBOL_GPL ( lzo1x_1_compress ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " LZO1X-1 Compressor " ) ;