# Copyright (c) 2016-present, Gregory Szorc # All rights reserved. # # This software may be modified and distributed under the terms # of the BSD license. See the LICENSE file for details. from __future__ import absolute_import import cffi import os HERE = os.path.abspath(os.path.dirname(__file__)) SOURCES = ['zstd/%s' % p for p in ( 'common/entropy_common.c', 'common/error_private.c', 'common/fse_decompress.c', 'common/xxhash.c', 'common/zstd_common.c', 'compress/fse_compress.c', 'compress/huf_compress.c', 'compress/zbuff_compress.c', 'compress/zstd_compress.c', 'decompress/huf_decompress.c', 'decompress/zbuff_decompress.c', 'decompress/zstd_decompress.c', 'dictBuilder/divsufsort.c', 'dictBuilder/zdict.c', )] INCLUDE_DIRS = [os.path.join(HERE, d) for d in ( 'zstd', 'zstd/common', 'zstd/compress', 'zstd/decompress', 'zstd/dictBuilder', )] with open(os.path.join(HERE, 'zstd', 'zstd.h'), 'rb') as fh: zstd_h = fh.read() ffi = cffi.FFI() ffi.set_source('_zstd_cffi', ''' /* needed for typedefs like U32 references in zstd.h */ #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY #include "zstd.h" ''', sources=SOURCES, include_dirs=INCLUDE_DIRS) # Rather than define the API definitions from zstd.h inline, munge the # source in a way that cdef() will accept. lines = zstd_h.splitlines() lines = [l.rstrip() for l in lines if l.strip()] # Strip preprocessor directives - they aren't important for our needs. lines = [l for l in lines if not l.startswith((b'#if', b'#else', b'#endif', b'#include'))] # Remove extern C block lines = [l for l in lines if l not in (b'extern "C" {', b'}')] # The version #defines don't parse and aren't necessary. Strip them. lines = [l for l in lines if not l.startswith(( b'#define ZSTD_H_235446', b'#define ZSTD_LIB_VERSION', b'#define ZSTD_QUOTE', b'#define ZSTD_EXPAND_AND_QUOTE', b'#define ZSTD_VERSION_STRING', b'#define ZSTD_VERSION_NUMBER'))] # The C parser also doesn't like some constant defines referencing # other constants. # TODO we pick the 64-bit constants here. We should assert somewhere # we're compiling for 64-bit. def fix_constants(l): if l.startswith(b'#define ZSTD_WINDOWLOG_MAX '): return b'#define ZSTD_WINDOWLOG_MAX 27' elif l.startswith(b'#define ZSTD_CHAINLOG_MAX '): return b'#define ZSTD_CHAINLOG_MAX 28' elif l.startswith(b'#define ZSTD_HASHLOG_MAX '): return b'#define ZSTD_HASHLOG_MAX 27' elif l.startswith(b'#define ZSTD_CHAINLOG_MAX '): return b'#define ZSTD_CHAINLOG_MAX 28' elif l.startswith(b'#define ZSTD_CHAINLOG_MIN '): return b'#define ZSTD_CHAINLOG_MIN 6' elif l.startswith(b'#define ZSTD_SEARCHLOG_MAX '): return b'#define ZSTD_SEARCHLOG_MAX 26' elif l.startswith(b'#define ZSTD_BLOCKSIZE_ABSOLUTEMAX '): return b'#define ZSTD_BLOCKSIZE_ABSOLUTEMAX 131072' else: return l lines = map(fix_constants, lines) # ZSTDLIB_API isn't handled correctly. Strip it. lines = [l for l in lines if not l.startswith(b'# define ZSTDLIB_API')] def strip_api(l): if l.startswith(b'ZSTDLIB_API '): return l[len(b'ZSTDLIB_API '):] else: return l lines = map(strip_api, lines) source = b'\n'.join(lines) ffi.cdef(source.decode('latin1')) if __name__ == '__main__': ffi.compile()