/*
* Copyright 2015 Google Inc .
*
* SPDX - License - Identifier : GPL 2.0 + BSD - 3 - Clause
*/
# include <common.h>
# include <compiler.h>
# include <linux/kernel.h>
# include <linux/types.h>
static u16 LZ4_readLE16 ( const void * src ) { return le16_to_cpu ( * ( u16 * ) src ) ; }
static void LZ4_copy4 ( void * dst , const void * src ) { * ( u32 * ) dst = * ( u32 * ) src ; }
static void LZ4_copy8 ( void * dst , const void * src ) { * ( u64 * ) dst = * ( u64 * ) src ; }
typedef uint8_t BYTE ;
typedef uint16_t U16 ;
typedef uint32_t U32 ;
typedef int32_t S32 ;
typedef uint64_t U64 ;
# define FORCE_INLINE static inline __attribute__((always_inline))
/* Unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */
# include "lz4.c" /* #include for inlining, do not link! */
# define LZ4F_MAGIC 0x184D2204
struct lz4_frame_header {
u32 magic ;
union {
u8 flags ;
struct {
u8 reserved0 : 2 ;
u8 has_content_checksum : 1 ;
u8 has_content_size : 1 ;
u8 has_block_checksum : 1 ;
u8 independent_blocks : 1 ;
u8 version : 2 ;
} ;
} ;
union {
u8 block_descriptor ;
struct {
u8 reserved1 : 4 ;
u8 max_block_size : 3 ;
u8 reserved2 : 1 ;
} ;
} ;
/* + u64 content_size iff has_content_size is set */
/* + u8 header_checksum */
} __packed ;
struct lz4_block_header {
union {
u32 raw ;
struct {
u32 size : 31 ;
u32 not_compressed : 1 ;
} ;
} ;
/* + size bytes of data */
/* + u32 block_checksum iff has_block_checksum is set */
} __packed ;
int ulz4fn ( const void * src , size_t srcn , void * dst , size_t * dstn )
{
const void * end = dst + * dstn ;
const void * in = src ;
void * out = dst ;
int has_block_checksum ;
int ret ;
* dstn = 0 ;
{ /* With in-place decompression the header may become invalid later. */
const struct lz4_frame_header * h = in ;
if ( srcn < sizeof ( * h ) + sizeof ( u64 ) + sizeof ( u8 ) )
return - EINVAL ; /* input overrun */
/* We assume there's always only a single, standard frame. */
if ( le32_to_cpu ( h - > magic ) ! = LZ4F_MAGIC | | h - > version ! = 1 )
return - EPROTONOSUPPORT ; /* unknown format */
if ( h - > reserved0 | | h - > reserved1 | | h - > reserved2 )
return - EINVAL ; /* reserved must be zero */
if ( ! h - > independent_blocks )
return - EPROTONOSUPPORT ; /* we can't support this yet */
has_block_checksum = h - > has_block_checksum ;
in + = sizeof ( * h ) ;
if ( h - > has_content_size )
in + = sizeof ( u64 ) ;
in + = sizeof ( u8 ) ;
}
while ( 1 ) {
struct lz4_block_header b ;
b . raw = le32_to_cpu ( * ( u32 * ) in ) ;
in + = sizeof ( struct lz4_block_header ) ;
if ( in - src + b . size > srcn ) {
ret = - EINVAL ; /* input overrun */
break ;
}
if ( ! b . size ) {
ret = 0 ; /* decompression successful */
break ;
}
if ( b . not_compressed ) {
size_t size = min ( ( ptrdiff_t ) b . size , end - out ) ;
memcpy ( out , in , size ) ;
out + = size ;
if ( size < b . size ) {
ret = - ENOBUFS ; /* output overrun */
break ;
}
} else {
/* constant folding essential, do not touch params! */
ret = LZ4_decompress_generic ( in , out , b . size ,
end - out , endOnInputSize ,
full , 0 , noDict , out , NULL , 0 ) ;
if ( ret < 0 ) {
ret = - EPROTO ; /* decompression error */
break ;
}
out + = ret ;
}
in + = b . size ;
if ( has_block_checksum )
in + = sizeof ( u32 ) ;
}
* dstn = out - dst ;
return ret ;
}