contrib/python-zstandard/make_cffi.py
changeset 30435 b86a448a2965
child 30822 b54a2984cdd4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/python-zstandard/make_cffi.py	Thu Nov 10 22:15:58 2016 -0800
@@ -0,0 +1,110 @@
+# 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()