├── cffi ├── .gitignore ├── fibonacci.h ├── build_c_dynamic_lib.sh ├── fibonacci.c ├── fib_python.py ├── Readme.md └── test_cffi.py ├── pybind11 ├── .gitignore ├── fibonacci.h ├── build_pybind11.sh ├── Readme.md ├── fib_python.py ├── fibonacci.cpp └── test_pybind11.py ├── cython ├── integrate │ ├── .gitignore │ ├── build_cython.sh │ ├── setup.py │ ├── integrate.py │ ├── cyintegrate.pyx │ ├── cython_speedup.py │ └── Readme.md ├── optimize_python │ ├── .gitignore │ ├── build_cython.sh │ ├── build_cython.bat │ ├── setup.py │ ├── fib.pyx │ ├── Readme.md │ ├── fib_cython.py │ ├── fib_python.py │ └── cython_speedup.py ├── wrap_c │ ├── run_test.sh │ ├── .gitignore │ ├── test_cython_wrapper.py │ ├── fibonacci.h │ ├── cfib.pxd │ ├── cyfib.pyx │ ├── setup.py │ ├── build_cython.sh │ ├── fibonacci.c │ ├── Readme.md │ ├── fib_python.py │ └── cython_wrapper_speedup.py └── wrap_arrays │ ├── run_test.sh │ ├── .gitignore │ ├── build_cython.sh │ ├── setup.py │ ├── cfastlz.pxd │ ├── cyfastlz.pyx │ ├── Readme.md │ ├── test_cython_wrapper.py │ ├── fastlz.h │ └── fastlz.c ├── swig ├── logger │ ├── .gitignore │ ├── build_swig.sh │ ├── example.i │ ├── setup.py │ ├── example.cpp │ ├── example.h │ ├── Readme.md │ └── runme.py ├── fibonacci │ ├── build_swig_python_wrapper.sh │ ├── build_swig_python_wrapper.bat │ ├── build_c.sh │ ├── build_swig_ruby_wrapper.sh │ ├── extconf.rb │ ├── .gitignore │ ├── fibonacci.h │ ├── fibonacci.i │ ├── test_swig.rb │ ├── CMakeLists.txt │ ├── setup.py │ ├── Readme.md │ ├── fib_python.py │ ├── test_swig.py │ └── fibonacci.c ├── structs │ ├── build_c.sh │ ├── build_swig_python_wrapper.sh │ ├── .gitignore │ ├── clean.sh │ ├── fibonacci.i │ ├── fibonacci.h │ ├── Readme.md │ ├── setup.py │ ├── fib_python.py │ ├── test_swig.py │ └── fibonacci.c └── fastlz │ ├── .gitignore │ ├── build_swig_python_wrapper.sh │ ├── build_fastlz_lib.sh │ ├── Compress.i │ ├── setup.py │ ├── Compress.h │ ├── Readme.md │ ├── Compress.cpp │ ├── test_swig.py │ ├── fastlz.h │ └── fastlz.c ├── Interfacing_C_C++_with_Python.pdf ├── Interfacing_C_C++_with_Python-Exercises.pdf ├── Pipfile ├── LICENSE ├── .gitignore ├── Prerequisites.md └── README.md /cffi/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | -------------------------------------------------------------------------------- /pybind11/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.dSYM 4 | -------------------------------------------------------------------------------- /cython/integrate/.gitignore: -------------------------------------------------------------------------------- 1 | *.c 2 | *.html 3 | 4 | -------------------------------------------------------------------------------- /cython/optimize_python/.gitignore: -------------------------------------------------------------------------------- 1 | fib.c 2 | fib.html 3 | -------------------------------------------------------------------------------- /cython/wrap_c/run_test.sh: -------------------------------------------------------------------------------- 1 | LD_LIBRARY_PATH=. py.test 2 | -------------------------------------------------------------------------------- /cython/wrap_arrays/run_test.sh: -------------------------------------------------------------------------------- 1 | LD_LIBRARY_PATH=. py.test 2 | -------------------------------------------------------------------------------- /cython/wrap_c/.gitignore: -------------------------------------------------------------------------------- 1 | cyfib.c 2 | cyfib.html 3 | fibonacci.o 4 | -------------------------------------------------------------------------------- /cython/integrate/build_cython.sh: -------------------------------------------------------------------------------- 1 | python setup.py build_ext --inplace 2 | -------------------------------------------------------------------------------- /cython/wrap_arrays/.gitignore: -------------------------------------------------------------------------------- 1 | cyfastlz.c 2 | cyfastlz.html 3 | *.o 4 | -------------------------------------------------------------------------------- /cython/optimize_python/build_cython.sh: -------------------------------------------------------------------------------- 1 | python setup.py build_ext --inplace 2 | -------------------------------------------------------------------------------- /swig/logger/.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | example.py 3 | example_wrap.* 4 | 5 | -------------------------------------------------------------------------------- /cython/optimize_python/build_cython.bat: -------------------------------------------------------------------------------- 1 | python setup.py build_ext --inplace 2 | -------------------------------------------------------------------------------- /swig/fibonacci/build_swig_python_wrapper.sh: -------------------------------------------------------------------------------- 1 | python setup.py build_ext --inplace 2 | -------------------------------------------------------------------------------- /swig/fibonacci/build_swig_python_wrapper.bat: -------------------------------------------------------------------------------- 1 | python setup.py build_ext --inplace 2 | -------------------------------------------------------------------------------- /swig/structs/build_c.sh: -------------------------------------------------------------------------------- 1 | cc -std=c11 -Wall -Wextra -g0 -O3 -o fibonacci_c fibonacci.c 2 | -------------------------------------------------------------------------------- /swig/fibonacci/build_c.sh: -------------------------------------------------------------------------------- 1 | cc -std=c11 -Wall -Wextra -g0 -O3 -o fibonacci_c fibonacci.c 2 | -------------------------------------------------------------------------------- /swig/fastlz/.gitignore: -------------------------------------------------------------------------------- 1 | fastlz_c 2 | Compress.py 3 | Compress_wrap.cpp 4 | *.egg-info 5 | *.o 6 | -------------------------------------------------------------------------------- /swig/logger/build_swig.sh: -------------------------------------------------------------------------------- 1 | python setup.py build_ext 2 | python setup.py install --install-platlib=. 3 | -------------------------------------------------------------------------------- /swig/fastlz/build_swig_python_wrapper.sh: -------------------------------------------------------------------------------- 1 | python setup.py build_ext 2 | python setup.py install --install-platlib=. 3 | -------------------------------------------------------------------------------- /swig/fibonacci/build_swig_ruby_wrapper.sh: -------------------------------------------------------------------------------- 1 | swig -ruby fibonacci.i 2 | ruby extconf.rb 3 | make 4 | sudo make install 5 | -------------------------------------------------------------------------------- /swig/structs/build_swig_python_wrapper.sh: -------------------------------------------------------------------------------- 1 | python setup.py build_ext 2 | python setup.py install --install-platlib=. 3 | -------------------------------------------------------------------------------- /Interfacing_C_C++_with_Python.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tleonhardt/Python_Interface_Cpp/HEAD/Interfacing_C_C++_with_Python.pdf -------------------------------------------------------------------------------- /swig/fibonacci/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | $CFLAGS << ' -std=c11 -Wall -Wextra -g0 -O3 ' 4 | 5 | create_makefile('fibonacci') 6 | -------------------------------------------------------------------------------- /swig/structs/.gitignore: -------------------------------------------------------------------------------- 1 | fibonacci_c 2 | fibonacci.py 3 | fibonacci_wrap.c 4 | *.egg-info 5 | *.o 6 | Makefile 7 | *.bundle 8 | .RUBYARCHDIR.time 9 | -------------------------------------------------------------------------------- /Interfacing_C_C++_with_Python-Exercises.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tleonhardt/Python_Interface_Cpp/HEAD/Interfacing_C_C++_with_Python-Exercises.pdf -------------------------------------------------------------------------------- /swig/fibonacci/.gitignore: -------------------------------------------------------------------------------- 1 | fibonacci_c 2 | fibonacci.py 3 | fibonacci_wrap.c 4 | *.egg-info 5 | *.o 6 | Makefile 7 | *.bundle 8 | .RUBYARCHDIR.time 9 | -------------------------------------------------------------------------------- /pybind11/fibonacci.h: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.h 2 | * 3 | * Header declaring the public interface. 4 | */ 5 | 6 | #pragma once 7 | int compute_fibonacci(int n); 8 | -------------------------------------------------------------------------------- /cffi/fibonacci.h: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.h 2 | * 3 | * Header declaring the public interface. 4 | */ 5 | 6 | #pragma once 7 | extern int compute_fibonacci(int n); 8 | -------------------------------------------------------------------------------- /cython/wrap_c/test_cython_wrapper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import cyfib 3 | 4 | 5 | def test_valid(): 6 | assert (17711 == cyfib.compute_fibonacci_wrapper(20)) 7 | -------------------------------------------------------------------------------- /cython/wrap_c/fibonacci.h: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.h 2 | * 3 | * Header declaring the public interface. 4 | */ 5 | 6 | #pragma once 7 | extern int compute_fibonacci(int n); 8 | -------------------------------------------------------------------------------- /swig/fibonacci/fibonacci.h: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.h 2 | * 3 | * Header declaring the public interface. 4 | */ 5 | 6 | #pragma once 7 | extern int compute_fibonacci(int n); 8 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | ipython = "*" 8 | 9 | [packages] 10 | cffi = "*" 11 | cython = "*" 12 | pybind11 = "*" 13 | -------------------------------------------------------------------------------- /cffi/build_c_dynamic_lib.sh: -------------------------------------------------------------------------------- 1 | # Compile with position-independent code 2 | cc -std=c11 -c -Wall -Werror -g0 -O3 -fpic fibonacci.c 3 | 4 | # Create a shared library from an object file 5 | cc -shared -o libfibonacci.so fibonacci.o 6 | -------------------------------------------------------------------------------- /cython/wrap_c/cfib.pxd: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Cython declaration file specifying what function(s) we are using from which external C header. 4 | """ 5 | cdef extern from "fibonacci.h": 6 | int compute_fibonacci(int n) 7 | -------------------------------------------------------------------------------- /cython/wrap_c/cyfib.pyx: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # distutils: libraries = "fibonacci" 3 | # distutils: library_dirs = "." 4 | cimport cfib 5 | 6 | cpdef int compute_fibonacci_wrapper(int n): 7 | return cfib.compute_fibonacci(n) 8 | -------------------------------------------------------------------------------- /swig/fastlz/build_fastlz_lib.sh: -------------------------------------------------------------------------------- 1 | # Compile fastlz.c with position-independent code 2 | cc -std=c11 -c -Wall -Werror -g0 -O3 -fpic fastlz.c 3 | 4 | # Create a shared library from an object file 5 | cc -shared -o libfastlz.so fastlz.o 6 | -------------------------------------------------------------------------------- /cython/wrap_c/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from setuptools import setup 3 | from Cython.Build import cythonize 4 | 5 | setup( 6 | name="cyfib", 7 | ext_modules=cythonize('cyfib.pyx', compiler_directives={'embedsignature': True}), 8 | ) 9 | -------------------------------------------------------------------------------- /swig/fibonacci/fibonacci.i: -------------------------------------------------------------------------------- 1 | /* fibonacci.i */ 2 | %module fibonacci 3 | %{ 4 | /* Includes the header in the wrapper code */ 5 | #include "fibonacci.h" 6 | %} 7 | 8 | /* Parse the header file to generate wrappers */ 9 | %include "fibonacci.h" 10 | -------------------------------------------------------------------------------- /swig/fibonacci/test_swig.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # The feature name begins with a lowercase letter... 4 | require 'fibonacci' 5 | 6 | # ... but the module name begins with an uppercase letter 7 | puts "fib(20): #{Fibonacci.compute_fibonacci(20)}" 8 | -------------------------------------------------------------------------------- /swig/structs/clean.sh: -------------------------------------------------------------------------------- 1 | # Remove the C binary 2 | rm fibonacci_c 3 | 4 | # Cleanup SWIG wrapper stuff 5 | rm -rf build 6 | rm *.egg-info 7 | rm *.so 8 | rm fibonacci.py 9 | rm fibonacci_wrap.c 10 | 11 | # Remove *.pyc files 12 | rm -rf __pycache__ 13 | -------------------------------------------------------------------------------- /cython/wrap_arrays/build_cython.sh: -------------------------------------------------------------------------------- 1 | # Compile fastlz.c with position-independent code 2 | cc -std=c11 -c -Wall -Werror -g0 -O3 -fpic fastlz.c 3 | 4 | # Create a shared library from an object file 5 | cc -shared -o libfastlz.so fastlz.o 6 | 7 | # Build the Cython wrapper using setuptools 8 | python setup.py build_ext --inplace 9 | -------------------------------------------------------------------------------- /cython/wrap_c/build_cython.sh: -------------------------------------------------------------------------------- 1 | # Compile fibonacci.c with position-independent code 2 | cc -std=c11 -c -Wall -Werror -g0 -O3 -fpic fibonacci.c 3 | 4 | # Create a shared library from an object file 5 | cc -shared -o libfibonacci.so fibonacci.o 6 | 7 | # Build the Cython wrapper using setuptools 8 | python setup.py build_ext --inplace 9 | -------------------------------------------------------------------------------- /cython/optimize_python/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from setuptools import setup 3 | from Cython.Build import cythonize 4 | import Cython.Compiler.Options 5 | 6 | Cython.Compiler.Options.annotate = True 7 | 8 | setup( 9 | name="fib", 10 | ext_modules=cythonize('fib.pyx', compiler_directives={'embedsignature': True}), 11 | ) 12 | -------------------------------------------------------------------------------- /swig/structs/fibonacci.i: -------------------------------------------------------------------------------- 1 | /* fibonacci.i */ 2 | %module fibonacci 3 | %{ 4 | /* Includes the header in the wrapper code */ 5 | #include "fibonacci.h" 6 | %} 7 | 8 | // Include types such as uint32_t, uint8_t, etc. 9 | %include "stdint.i" 10 | 11 | /* Parse the header file to generate wrappers */ 12 | %include "fibonacci.h" 13 | -------------------------------------------------------------------------------- /cython/integrate/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from setuptools import setup 3 | from Cython.Build import cythonize 4 | import Cython.Compiler.Options 5 | 6 | Cython.Compiler.Options.annotate = True 7 | 8 | setup( 9 | name="cyintegrate", 10 | ext_modules=cythonize('cyintegrate.pyx', compiler_directives={'embedsignature': True}), 11 | ) 12 | -------------------------------------------------------------------------------- /cython/wrap_arrays/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from setuptools import setup 3 | from Cython.Build import cythonize 4 | import Cython.Compiler.Options 5 | 6 | Cython.Compiler.Options.annotate = True 7 | 8 | setup( 9 | name="cyfastlz", 10 | ext_modules=cythonize('cyfastlz.pyx', compiler_directives={'embedsignature': True}), 11 | ) 12 | -------------------------------------------------------------------------------- /pybind11/build_pybind11.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Path to pybind11 headers 3 | PYBIND11_INCLUDE=/usr/local/include 4 | 5 | # On Linux and Mac OS, this example can be compiled using the following command (for Windows, use CMake) 6 | c++ -O3 -shared -std=c++11 -Wall -fPIC -I ${PYBIND11_INCLUDE} `python-config --cflags --ldflags` fibonacci.cpp -o fibonacci.so 7 | -------------------------------------------------------------------------------- /cython/wrap_arrays/cfastlz.pxd: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Cython declaration file specifying what function(s) we are using from which external C header. 4 | """ 5 | from libc.stdint cimport uint8_t 6 | 7 | 8 | cdef extern from "fastlz.h": 9 | # TODO: Copy function prototypes you want to wrap from the C header file here (without ending semicolon) 10 | 11 | -------------------------------------------------------------------------------- /cffi/fibonacci.c: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.c 2 | * 3 | * Implementation of the algorithm in C to be compiled to a dynamic library 4 | */ 5 | 6 | int compute_fibonacci(int n) 7 | { 8 | int temp; 9 | int a = 1; 10 | int b = 1; 11 | for (int x=0; x 9 | 10 | typedef uint32_t dtype; 11 | 12 | struct MsgMetaData 13 | { 14 | dtype type; // Message Type 15 | dtype job; // Job ID 16 | dtype size; // Message Size (not including the metadata header) 17 | }; 18 | 19 | extern int compute_fibonacci(int n); 20 | 21 | struct MsgMetaData populate_struct(dtype msgType, dtype jobId, dtype msgSize); 22 | -------------------------------------------------------------------------------- /pybind11/Readme.md: -------------------------------------------------------------------------------- 1 | # Interfacing with C++ from Python using pybind11 2 | Example code for interfacing with C++ code from Python using pybind11. 3 | 4 | ## Building 5 | Build the pybind11 Python wrapper using the following shell script: 6 | 7 | ./build_pybind11.sh 8 | 9 | ## Evaluating 10 | Compare the results of a pure Python implementation to that of the pybind11 wrapper using: 11 | 12 | ./test_pybind11.py 13 | 14 | ## Code Files 15 | * Pure Python implementation 16 | * fib_python.py 17 | * C++ implementation 18 | * fibonacci.h/.cpp 19 | * pybind11 wrapper 20 | * test_pybind11.py 21 | -------------------------------------------------------------------------------- /swig/fastlz/Compress.i: -------------------------------------------------------------------------------- 1 | /* Compress.i */ 2 | %module Compress 3 | %{ 4 | /* Includes the header in the wrapper code */ 5 | #include "Compress.h" 6 | %} 7 | 8 | // SWIG library file for ISO C99 types such as uint32_t 9 | %include "stdint.i" 10 | 11 | // Provide support for the C++ vector class in the STL 12 | %include "std_vector.i" 13 | 14 | // Instantiate a Python class called VectorUint8 which wraps a C++ vector STL container of bytes 15 | namespace std 16 | { 17 | %template(VectorUint8) vector; 18 | } 19 | 20 | /* Parse the header file to generate wrappers */ 21 | %include "Compress.h" 22 | -------------------------------------------------------------------------------- /swig/fibonacci/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is a CMake example for SWIG and Python 2 | cmake_minimum_required(VERSION 3.8) 3 | 4 | FIND_PACKAGE(SWIG REQUIRED) 5 | INCLUDE(${SWIG_USE_FILE}) 6 | 7 | FIND_PACKAGE(PythonLibs) 8 | INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH}) 9 | 10 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) 11 | 12 | SET(CMAKE_SWIG_FLAGS "") 13 | 14 | SET_SOURCE_FILES_PROPERTIES(fibonacci.i PROPERTIES SWIG_FLAGS "-includeall") 15 | 16 | SWIG_ADD_LIBRARY(fibonacci 17 | LANGUAGE python 18 | SOURCES fibonacci.i fibonacci.c) 19 | 20 | SWIG_LINK_LIBRARIES(fibonacci ${PYTHON_LIBRARIES}) 21 | -------------------------------------------------------------------------------- /swig/structs/Readme.md: -------------------------------------------------------------------------------- 1 | # Interfacing with C code from Python using SWIG 2 | Example code for interfacing with C code from Python using SWIG. 3 | 4 | ## Building 5 | Build the SWIG wrapper using the following shell script: 6 | 7 | ./build_swig_python_wrapper.sh 8 | 9 | ## Evaluating 10 | Compare the results of a pure Python implementation to that of the SWIG wrapper using: 11 | 12 | ./test_swig.py 13 | 14 | ## Code Files 15 | * Pure Python implementation 16 | * fib_python.py 17 | * C implementation 18 | * fibonacci.h/.c 19 | * SWIG 20 | * Interface File 21 | * fibonacci.i 22 | * Distutils script 23 | * setup.py 24 | -------------------------------------------------------------------------------- /swig/fibonacci/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from distutils.core import setup, Extension 3 | 4 | name = "fibonacci" # name of the module 5 | version = "1.0" # the module's version number 6 | 7 | setup(name=name, version=version, 8 | # distutils detects .i files and compiles them automatically 9 | ext_modules=[Extension(name='_{}'.format(name), # SWIG requires _ as a prefix for the module name 10 | sources=["fibonacci.i", "fibonacci.c"], 11 | include_dirs=[], 12 | extra_compile_args=["-std=c11"], 13 | swig_opts=[]) 14 | ]) 15 | -------------------------------------------------------------------------------- /swig/logger/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from distutils.core import setup, Extension 3 | 4 | name = "example" # name of the module 5 | version = "1.0" # the module's version number 6 | 7 | setup(name=name, version=version, 8 | # distutils detects .i files and compiles them automatically 9 | ext_modules=[Extension(name='_{}'.format(name), # SWIG requires _ as a prefix for the module name 10 | sources=["example.i", "example.cpp"], 11 | include_dirs=[], 12 | extra_compile_args=["-std=c++11"], 13 | swig_opts=['-c++']) 14 | ]) 15 | -------------------------------------------------------------------------------- /swig/structs/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from distutils.core import setup, Extension 3 | 4 | name = "fibonacci" # name of the module 5 | version = "1.0" # the module's version number 6 | 7 | setup(name=name, version=version, 8 | # distutils detects .i files and compiles them automatically 9 | ext_modules=[Extension(name='_{}'.format(name), # SWIG requires _ as a prefix for the module name 10 | sources=["fibonacci.i", "fibonacci.c"], 11 | include_dirs=[], 12 | extra_compile_args=["-std=c11"], 13 | swig_opts=[]) 14 | ]) 15 | -------------------------------------------------------------------------------- /swig/logger/example.cpp: -------------------------------------------------------------------------------- 1 | /* File : example.cxx */ 2 | 3 | #include "example.h" 4 | 5 | Logger::~Logger() 6 | { 7 | std::cout << "Logger::~Logger()" << std:: endl; 8 | } 9 | 10 | void Logger::log(int level, std::string message) 11 | { 12 | std::cout << "C++ Logger - Level: "; 13 | 14 | switch(level) 15 | { 16 | case LOG_ERROR: 17 | std::cout << "ERROR"; 18 | break; 19 | case LOG_WARNING: 20 | std::cout << "WARNING"; 21 | break; 22 | case LOG_INFO: 23 | std::cout << "INFO"; 24 | break; 25 | default: 26 | std::cout << "UNKNOWN"; 27 | break; 28 | } 29 | 30 | std::cout << ", message: " << message << std::endl; 31 | } 32 | -------------------------------------------------------------------------------- /cython/wrap_c/Readme.md: -------------------------------------------------------------------------------- 1 | # Wrapping existing C code using Cython 2 | Example code for wrapping an existing C dynamic library in Python by using Cython. 3 | 4 | ## Building 5 | Build the dynamic library and Cython wrapper using the following shell script: 6 | 7 | ./build_cython.sh 8 | 9 | ## Evaluating 10 | Compare the results of a pure Python implementation to that of the Cython wrapper using: 11 | 12 | ./cython_wrapper_speedup.py 13 | 14 | ## Code Files 15 | * Pure Python implementation 16 | * fib_python.py 17 | * C implementation 18 | * fibonacci.h/.c 19 | * Cython wrapper 20 | * Cython declaration 21 | * cfib.pxd 22 | * Cython wrapper 23 | * cyfib.pyx 24 | * Setuptools script 25 | * setup.py 26 | -------------------------------------------------------------------------------- /swig/fastlz/setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from distutils.core import setup, Extension 3 | 4 | name = "Compress" # name of the module 5 | version = "1.0" # the module's version number 6 | 7 | setup(name=name, version=version, 8 | # distutils detects .i files and compiles them automatically 9 | ext_modules=[Extension(name='_{}'.format(name), # SWIG requires _ as a prefix for the module name 10 | sources=["Compress.i", "Compress.cpp"], 11 | include_dirs=[], 12 | libraries=['fastlz'], 13 | library_dirs=['.'], 14 | extra_compile_args=["-std=c++11"], 15 | swig_opts=['-c++']) 16 | ]) 17 | -------------------------------------------------------------------------------- /cython/optimize_python/Readme.md: -------------------------------------------------------------------------------- 1 | # Optimizing existing Python code using Cython 2 | Example code for optimizing existing Python code by selectively compiling a portion of it to C using Cython. 3 | 4 | ## Building 5 | 6 | ### Mac OS X or Linux 7 | Build the Cython extension using the following shell script: 8 | 9 | ./build_cython.sh 10 | 11 | ### Windows 12 | Build the Cython extension using the following batch file: 13 | 14 | build_cython.bat 15 | 16 | ## Evaluating 17 | Compare the results of a pure Python implementation to that of the Cython extension: 18 | 19 | ./cython_speedup.py 20 | 21 | ## Code Files 22 | * Pure Python implementation 23 | * fib_python.py 24 | * Cython implementation 25 | * Cython code 26 | * fib.pyx 27 | * Setuptools script 28 | * setup.py 29 | -------------------------------------------------------------------------------- /swig/fibonacci/Readme.md: -------------------------------------------------------------------------------- 1 | # Interfacing with C code from Python using SWIG 2 | Example code for interfacing with C code from Python using SWIG. 3 | 4 | 5 | ## Building 6 | 7 | ### Mac OS X or Linus 8 | Build the SWIG wrapper using the following shell script: 9 | 10 | ./build_swig_python_wrapper.sh 11 | 12 | ### Windows 13 | Build the SWIG wrapper using the following batch file: 14 | 15 | build_swig_python_wrapper.bat 16 | 17 | 18 | ## Evaluating 19 | Compare the results of a pure Python implementation to that of the SWIG wrapper using: 20 | 21 | ./test_swig.py 22 | 23 | ## Code Files 24 | * Pure Python implementation 25 | * fib_python.py 26 | * C implementation 27 | * fibonacci.h/.c 28 | * SWIG 29 | * Interface File 30 | * fibonacci.i 31 | * Distutils script 32 | * setup.py 33 | -------------------------------------------------------------------------------- /swig/logger/example.h: -------------------------------------------------------------------------------- 1 | /* File : example.h */ 2 | 3 | #include 4 | #include 5 | 6 | 7 | enum LOG_LEVEL 8 | { 9 | LOG_INFO = 20, 10 | LOG_WARNING = 30, 11 | LOG_ERROR = 40 12 | }; 13 | 14 | 15 | class Logger 16 | { 17 | public: 18 | virtual ~Logger(); 19 | virtual void log(int level, std::string message); 20 | }; 21 | 22 | 23 | class Log 24 | { 25 | private: 26 | Logger *_logger; 27 | public: 28 | Log(): _logger(nullptr) {} 29 | ~Log() { delLogger(); } 30 | void delLogger() { delete _logger; _logger = nullptr; } 31 | void setLogger(Logger *cb) { delLogger(); _logger = cb; } 32 | void log(int level, std::string msg) { if (_logger) _logger->log(level, msg); } 33 | void err(std::string msg) {log(LOG_ERROR, msg);} 34 | void war(std::string msg) {log(LOG_WARNING, msg);} 35 | void inf(std::string msg) {log(LOG_INFO, msg);} 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /cython/integrate/integrate.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Pure Python code for numerically integrating a function. 4 | """ 5 | from math import cos 6 | 7 | 8 | def f(x): 9 | """Example function in one variable. 10 | 11 | :param x: float - point we wish to evaluate the function at 12 | :return: float - function value at point x 13 | """ 14 | return cos(x) 15 | 16 | 17 | def integrate_f(a, b, N): 18 | """Numerically integrate function f starting at point a and going to point b, using N rectangles. 19 | 20 | :param a: float - starting point 21 | :param b: float - ending point 22 | :param N: int - number of points to use in the rectangluar approximation to the integral 23 | :return: float - approximation to the true integral, which improves as N increases 24 | """ 25 | s = 0.0 26 | dx = (b-a)/N 27 | for i in range(N): 28 | s += f(a+i*dx) 29 | return s * dx 30 | -------------------------------------------------------------------------------- /cython/integrate/cyintegrate.pyx: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | Pure Python code for numerically integrating a function. 4 | """ 5 | from math import cos 6 | 7 | 8 | def f(x): 9 | """Example function in one variable. 10 | 11 | :param x: float - point we wish to evaluate the function at 12 | :return: float - function value at point x 13 | """ 14 | return cos(x) 15 | 16 | 17 | def integrate_f(a, b, N): 18 | """Numerically integrate function f starting at point a and going to point b, using N rectangles. 19 | 20 | :param a: float - starting point 21 | :param b: float - ending point 22 | :param N: int - number of points to use in the rectangluar approximation to the integral 23 | :return: float - approximation to the true integral, which improves as N increases 24 | """ 25 | s = 0.0 26 | dx = (b-a)/N 27 | for i in range(N): 28 | s += f(a+i*dx) 29 | return s * dx 30 | -------------------------------------------------------------------------------- /swig/fastlz/Compress.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // Convenience wrappers around fastlz C functions which use STL vectors instead of C arrays 7 | 8 | /** Compress an input vector of bytes and store it in an output vector. 9 | * 10 | * @param in - std::vector input vector of uncompressed bytes 11 | * @param out - std::vector output vector of compressed bytes 12 | * @return bool - true if successful, false otherwise 13 | */ 14 | bool Compress(const std::vector& in, std::vector& out); 15 | 16 | /** Decompress an input vector of bytes and store it in an output vector. 17 | * 18 | * @param in - std::vector input vector of compressed bytes 19 | * @param out - std::vector output vector of uncompressed bytes 20 | * @return bool - true if successful, false otherwise 21 | */ 22 | bool Decompress(const std::vector& in, std::vector& out); 23 | -------------------------------------------------------------------------------- /cython/optimize_python/fib_cython.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Python wrapper to time the Cython implementation for computing the nth fibonacci number 4 | in a non-recursive fashion and compare it to the pure Python implementation. 5 | """ 6 | from fib import compute_fibonacci_cython 7 | 8 | if __name__ == '__main__': 9 | import sys 10 | import timeit 11 | 12 | n = 20 13 | try: 14 | n = int(sys.argv[1]) 15 | except Exception: 16 | pass 17 | 18 | fib_n = compute_fibonacci_cython(n) 19 | 20 | number_of_times = 100000 21 | try: 22 | number_of_times = int(sys.argv[2]) 23 | except Exception: 24 | pass 25 | 26 | total_time = timeit.timeit("compute_fibonacci_cython({})".format(n), 27 | setup="from fib import compute_fibonacci_cython", 28 | number=number_of_times) 29 | avg_time = total_time / number_of_times 30 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Todd Leonhardt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /swig/fastlz/Readme.md: -------------------------------------------------------------------------------- 1 | # Wrapping C++ code using STL containers with SWIG 2 | This code uses SWIG to wrap C++ functions which use STL container classes. It demonstrates how to wrap 3 | C++ code which uses STL container class and instantiate STL container wrappers in Python. In the process 4 | it also demonstrates how to link SWIG wrapper code to dynamic libraries. 5 | 6 | ## Building 7 | First build the fastlz C compression code into a dynamic library: 8 | 9 | ./build_fastlz_lib.sh 10 | 11 | Then build the SWIG wrapper using the following shell script: 12 | 13 | ./build_swig_python_wrapper.sh 14 | 15 | ## Evaluating 16 | Test a full round-trip compression and decompression using the wrapper with py.test 17 | 18 | py.test -v 19 | 20 | ## Code Files 21 | * C compression code which gets built into a dynamic library 22 | * fastlz.h/.c 23 | * C++ compression wrapper which wraps the C library and gets wrapped by SWIG 24 | * Compress.h/.cpp 25 | * SWIG 26 | * Interface File 27 | * Compress.i 28 | * Distutils script 29 | * setup.py 30 | 31 | ## Exercise 32 | Fill in the **TODO:** blocks present in **test_swig.py** 33 | -------------------------------------------------------------------------------- /cffi/fib_python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Pure Python implementation for computing the nth fibonacci number in a 4 | non-recursive fashion. 5 | """ 6 | 7 | 8 | def compute_fibonacci(n): 9 | """ 10 | Computes fibonacci sequence 11 | """ 12 | a = 1 13 | b = 1 14 | intermediate = 0 15 | for x in range(n): 16 | intermediate = a 17 | a = a + b 18 | b = intermediate 19 | return a 20 | 21 | 22 | if __name__ == '__main__': 23 | import sys 24 | import timeit 25 | 26 | n = 20 27 | try: 28 | n = int(sys.argv[1]) 29 | except Exception: 30 | pass 31 | 32 | fib_n = compute_fibonacci(n) 33 | 34 | number_of_times = 100000 35 | try: 36 | number_of_times = int(sys.argv[2]) 37 | except Exception: 38 | pass 39 | 40 | total_time = timeit.timeit("compute_fibonacci({})".format(n), 41 | setup="from __main__ import compute_fibonacci", 42 | number=number_of_times) 43 | avg_time = total_time / number_of_times 44 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 45 | -------------------------------------------------------------------------------- /pybind11/fib_python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Pure Python implementation for computing the nth fibonacci number in a 4 | non-recursive fashion. 5 | """ 6 | 7 | 8 | def compute_fibonacci(n): 9 | """ 10 | Computes fibonacci sequence 11 | """ 12 | a = 1 13 | b = 1 14 | intermediate = 0 15 | for x in range(n): 16 | intermediate = a 17 | a = a + b 18 | b = intermediate 19 | return a 20 | 21 | 22 | if __name__ == '__main__': 23 | import sys 24 | import timeit 25 | 26 | n = 20 27 | try: 28 | n = int(sys.argv[1]) 29 | except Exception: 30 | pass 31 | 32 | fib_n = compute_fibonacci(n) 33 | 34 | number_of_times = 100000 35 | try: 36 | number_of_times = int(sys.argv[2]) 37 | except Exception: 38 | pass 39 | 40 | total_time = timeit.timeit("compute_fibonacci({})".format(n), 41 | setup="from __main__ import compute_fibonacci", 42 | number=number_of_times) 43 | avg_time = total_time / number_of_times 44 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 45 | -------------------------------------------------------------------------------- /cython/wrap_c/fib_python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Pure Python implementation for computing the nth fibonacci number in a 4 | non-recursive fashion. 5 | """ 6 | 7 | 8 | def compute_fibonacci(n): 9 | """ 10 | Computes fibonacci sequence 11 | """ 12 | a = 1 13 | b = 1 14 | intermediate = 0 15 | for x in range(n): 16 | intermediate = a 17 | a = a + b 18 | b = intermediate 19 | return a 20 | 21 | 22 | if __name__ == '__main__': 23 | import sys 24 | import timeit 25 | 26 | n = 20 27 | try: 28 | n = int(sys.argv[1]) 29 | except Exception: 30 | pass 31 | 32 | fib_n = compute_fibonacci(n) 33 | 34 | number_of_times = 100000 35 | try: 36 | number_of_times = int(sys.argv[2]) 37 | except Exception: 38 | pass 39 | 40 | total_time = timeit.timeit("compute_fibonacci({})".format(n), 41 | setup="from __main__ import compute_fibonacci", 42 | number=number_of_times) 43 | avg_time = total_time / number_of_times 44 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 45 | -------------------------------------------------------------------------------- /swig/structs/fib_python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Pure Python implementation for computing the nth fibonacci number in a 4 | non-recursive fashion. 5 | """ 6 | 7 | 8 | def compute_fibonacci(n): 9 | """ 10 | Computes fibonacci sequence 11 | """ 12 | a = 1 13 | b = 1 14 | intermediate = 0 15 | for x in range(n): 16 | intermediate = a 17 | a = a + b 18 | b = intermediate 19 | return a 20 | 21 | 22 | if __name__ == '__main__': 23 | import sys 24 | import timeit 25 | 26 | n = 20 27 | try: 28 | n = int(sys.argv[1]) 29 | except Exception: 30 | pass 31 | 32 | fib_n = compute_fibonacci(n) 33 | 34 | number_of_times = 100000 35 | try: 36 | number_of_times = int(sys.argv[2]) 37 | except Exception: 38 | pass 39 | 40 | total_time = timeit.timeit("compute_fibonacci({})".format(n), 41 | setup="from __main__ import compute_fibonacci", 42 | number=number_of_times) 43 | avg_time = total_time / number_of_times 44 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 45 | -------------------------------------------------------------------------------- /swig/fibonacci/fib_python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Pure Python implementation for computing the nth fibonacci number in a 4 | non-recursive fashion. 5 | """ 6 | 7 | 8 | def compute_fibonacci(n): 9 | """ 10 | Computes fibonacci sequence 11 | """ 12 | a = 1 13 | b = 1 14 | intermediate = 0 15 | for x in range(n): 16 | intermediate = a 17 | a = a + b 18 | b = intermediate 19 | return a 20 | 21 | 22 | if __name__ == '__main__': 23 | import sys 24 | import timeit 25 | 26 | n = 20 27 | try: 28 | n = int(sys.argv[1]) 29 | except Exception: 30 | pass 31 | 32 | fib_n = compute_fibonacci(n) 33 | 34 | number_of_times = 100000 35 | try: 36 | number_of_times = int(sys.argv[2]) 37 | except Exception: 38 | pass 39 | 40 | total_time = timeit.timeit("compute_fibonacci({})".format(n), 41 | setup="from __main__ import compute_fibonacci", 42 | number=number_of_times) 43 | avg_time = total_time / number_of_times 44 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 45 | -------------------------------------------------------------------------------- /cython/optimize_python/fib_python.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Pure Python implementation for computing the nth fibonacci number in a 4 | non-recursive fashion. 5 | """ 6 | 7 | 8 | def compute_fibonacci(n): 9 | """ 10 | Computes fibonacci sequence 11 | """ 12 | a = 1 13 | b = 1 14 | intermediate = 0 15 | for x in range(n): 16 | intermediate = a 17 | a = a + b 18 | b = intermediate 19 | return a 20 | 21 | 22 | if __name__ == '__main__': 23 | import sys 24 | import timeit 25 | 26 | n = 20 27 | try: 28 | n = int(sys.argv[1]) 29 | except Exception: 30 | pass 31 | 32 | fib_n = compute_fibonacci(n) 33 | 34 | number_of_times = 100000 35 | try: 36 | number_of_times = int(sys.argv[2]) 37 | except Exception: 38 | pass 39 | 40 | total_time = timeit.timeit("compute_fibonacci({})".format(n), 41 | setup="from __main__ import compute_fibonacci", 42 | number=number_of_times) 43 | avg_time = total_time / number_of_times 44 | print("fib({0}) = {1} [average execution time: {2:.2g} s]".format(n, fib_n, avg_time)) 45 | -------------------------------------------------------------------------------- /cffi/Readme.md: -------------------------------------------------------------------------------- 1 | # Interfacing with a dynamic library from Python using CFFI 2 | Example code for interfacing with a C dynamic library from Python using CFFI. 3 | 4 | ## Building 5 | Build the C dynamic library using the following shell script: 6 | 7 | ./build_c_dynamic_lib.sh 8 | 9 | ## Evaluating 10 | Compare the results of a pure Python implementation to that of the CFFI wrapper using: 11 | 12 | ./test_cffi.py 13 | 14 | ## Code Files 15 | * Pure Python implementation 16 | * fib_python.py 17 | * C implementation 18 | * fibonacci.h/.c 19 | * CFFI "wrapper" (there is no wrapper code, its all inline Python) 20 | * test_cffi.py 21 | 22 | 23 | ## Troubleshooting 24 | The ABI of the C API is generally not stable and may change between versions of Python. This can 25 | cause code which used to work to break when you upgrade either your version of Python or CFFI. 26 | 27 | If you get a Python traceback from an exception that contains something like "ImportError: dlopen", 28 | then you may have a mismatch between your version of Python and your version of CFFI. Try upgrading 29 | one or both. The **conda** package manager which ships with the Anaconda Python distro is convenient 30 | for this. 31 | -------------------------------------------------------------------------------- /pybind11/fibonacci.cpp: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.cpp 2 | * 3 | * Implementation of the algorithm in C++. 4 | * 5 | * For simplicity, we put both this function and the binding code into a single file. 6 | * 7 | * In practice, implementation and binding code will generally be located in separate files. 8 | */ 9 | 10 | #include 11 | 12 | int compute_fibonacci(int n) 13 | { 14 | int temp; 15 | int a = 1; 16 | int b = 1; 17 | for (int x=0; x // printf() 6 | #include // atoi() 7 | #include // clock_t, clock() 8 | 9 | int compute_fibonacci(int n) 10 | { 11 | int temp; 12 | int a = 1; 13 | int b = 1; 14 | for (int x=0; x 1) 28 | { 29 | n = atoi(argv[1]); 30 | } 31 | 32 | int number_of_times = 100000; 33 | if (argc > 2) 34 | { 35 | number_of_times = atoi(argv[2]); 36 | } 37 | 38 | int fib_n = compute_fibonacci(n); 39 | 40 | // Compute single execution time 41 | clock_t begin = clock(); 42 | compute_fibonacci(n); 43 | clock_t end = clock(); 44 | double single_time = (double)(end - begin) / CLOCKS_PER_SEC; 45 | printf("single execution time: %.2g s\n", single_time); 46 | 47 | // Compute average execution time over many times 48 | double time_spent = 0; 49 | for (int i = 0; i < number_of_times; i++) 50 | { 51 | begin = clock(); 52 | compute_fibonacci(n); 53 | end = clock(); 54 | time_spent += (double)(end - begin) / CLOCKS_PER_SEC; 55 | } 56 | double avg_time = time_spent / number_of_times; 57 | 58 | printf("fib(%d) = %d [average execution time: %.2g s]\n", n, fib_n, avg_time); 59 | 60 | return EXIT_SUCCESS; 61 | } 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # Mac OS X 92 | .DS_Store 93 | 94 | # IntelliJ IDEs such as PyCharm 95 | .idea 96 | 97 | # Pipenv 98 | Pipfile.lock 99 | .venv 100 | -------------------------------------------------------------------------------- /swig/structs/test_swig.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Python wrapper to time the SWIG wrapper for computing the nth fibonacci number 4 | in a non-recursive fashion and compare it to the pure Python implementation. 5 | """ 6 | import fibonacci 7 | import fib_python 8 | 9 | if __name__ == '__main__': 10 | import sys 11 | import timeit 12 | 13 | n = 20 14 | try: 15 | n = int(sys.argv[1]) 16 | except Exception: 17 | pass 18 | 19 | number_of_times = 100000 20 | try: 21 | number_of_times = int(sys.argv[2]) 22 | except Exception: 23 | pass 24 | 25 | fib_py = fib_python.compute_fibonacci(n) 26 | fib_swig = fibonacci.compute_fibonacci(n) 27 | if fib_py != fib_swig: 28 | raise (ValueError(fib_swig)) 29 | 30 | py_tot = timeit.timeit("compute_fibonacci({})".format(n), 31 | setup="from fib_python import compute_fibonacci", 32 | number=number_of_times) 33 | swig_tot = timeit.timeit("compute_fibonacci({})".format(n), 34 | setup="from fibonacci import compute_fibonacci", 35 | number=number_of_times) 36 | py_avg = py_tot / number_of_times 37 | swig_avg = swig_tot / number_of_times 38 | 39 | print("fib({}) = {}".format(n, fib_py)) 40 | print("Python average time: {0:.2g}".format(py_avg)) 41 | print("SWIG/C average time: {0:.2g}".format(swig_avg)) 42 | print("SWIG/C speedup: {0:.2g} times".format(py_avg / swig_avg)) 43 | 44 | # MetaData struct 45 | md = fibonacci.populate_struct(1, 2, 3) 46 | print("md.type = {}, md.job = {}, md.size = {}".format(md.type, md.job, md.size)) 47 | -------------------------------------------------------------------------------- /cython/wrap_arrays/Readme.md: -------------------------------------------------------------------------------- 1 | This code uses Cython to wrap C functions which take pointers to arrays as both IN and INOUT arguments. 2 | 3 | # Wrapping existing C code using Cython 4 | Example code for wrapping an existing C dynamic library in Python by using Cython. 5 | 6 | ## Building 7 | Build the dynamic library and Cython wrapper using the following shell script: 8 | 9 | ./build_cython.sh 10 | 11 | ## Evaluating 12 | Run pytest unit tests to verify everything is working as intended for the Cython wrapper: 13 | ./py.test -v 14 | 15 | ## Code Files 16 | * C implementation 17 | * fastlz.h/.c 18 | * Cython wrapper 19 | * Cython declaration 20 | * cfastlz.pxd 21 | * Cython wrapper 22 | * cyfastlz.pyx 23 | * Setuptools script 24 | * setup.py 25 | * pytest unit tests 26 | * test_cython_wrapper.py 27 | 28 | 29 | ## Exercise 30 | You need to fill in the **TODO:** blocks in **cfastlz.pxd** and **cyfastlz.pyx** 31 | 32 | Some useful things to keep in mind: 33 | 34 | 1. Read the documentation in fastlz.h to understand how the compress() and decompress() functions work. 35 | 2. You can cast from a Python type to a C type using syntax like the following: 36 | 37 | ```python 38 | in_buf 39 | ``` 40 | 3. In Python, you can use *slicing* to return a subarray, substring, etc using syntax similar to: 41 | 42 | ```python 43 | my_array = range(10) 44 | my_array[2:7] 45 | [2, 3, 4, 5, 6] 46 | ``` 47 | 48 | The slice includes the starting index, but excludes the ending index. 49 | 50 | You know you are done when the unit test passes. 51 | 52 | 53 | ### Solution 54 | The solution is on the **solutions** branch. Don't peak until you have given this an honest effort. 55 | -------------------------------------------------------------------------------- /swig/logger/runme.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | This file illustrates the cross language polymorphism using SWIG directors. 4 | """ 5 | from colorama import Fore 6 | import logging 7 | 8 | import example 9 | 10 | 11 | class PyLogger(example.Logger): 12 | 13 | def __init__(self): 14 | example.Logger.__init__(self) 15 | 16 | # Configure Python logging module root logger 17 | logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p', 18 | level=logging.INFO) 19 | 20 | def log(self, level, message): 21 | if level == logging.ERROR: 22 | message = Fore.RED + message + Fore.RESET 23 | elif level == logging.WARNING: 24 | message = Fore.YELLOW + message + Fore.RESET 25 | logging.log(level, message) 26 | 27 | 28 | if __name__ == '__main__': 29 | # Create an instance of the C++ Log class, which has a pointer to a C++ Logger class within it 30 | log = example.Log() 31 | 32 | # Add a simple C++ Logger (log owns the Logger, so we disown it first by clearing the .thisown flag). 33 | print("Adding and calling a normal C++ Logger") 34 | print("--------------------------------------") 35 | logger = example.Logger() 36 | logger.thisown = 0 37 | log.setLogger(logger) 38 | log.inf("Hello") 39 | log.war("World") 40 | log.log(5, "Yo") 41 | log.delLogger() 42 | 43 | # Add a Python Logger (log owns the logger, so we disown it first by calling __disown__). 44 | print() 45 | print("Adding and calling a Python Logger") 46 | print("----------------------------------") 47 | # TODO: Add a Python Logger (make sure to call .__disown__() on it 48 | # TODO: Set the Logger to this new Python Logger 49 | # TODO: Log a mix of ERROR, INFO, and WARNING messages 50 | # TODO: Don't forget to delete the logger to prevent a memory leak 51 | 52 | # All done. 53 | print() 54 | print("python exit") 55 | -------------------------------------------------------------------------------- /cffi/test_cffi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ Python wrapper to time the CFFI wrapper for computing the nth fibonacci number 4 | in a non-recursive fashion and compare it to the pure Python implementation. 5 | """ 6 | import os 7 | 8 | import cffi 9 | 10 | import fib_python 11 | 12 | 13 | if __name__ == '__main__': 14 | import sys 15 | import timeit 16 | 17 | n = 20 18 | try: 19 | n = int(sys.argv[1]) 20 | except Exception: 21 | pass 22 | 23 | number_of_times = 100000 24 | try: 25 | number_of_times = int(sys.argv[2]) 26 | except Exception: 27 | pass 28 | 29 | # The main top-level CFFI class that you instantiate once 30 | ffi = cffi.FFI() 31 | 32 | # Parses the given C source. This registers all declared functions. 33 | ffi.cdef('int compute_fibonacci(int n);') 34 | 35 | # Load and return a dynamic library. The standard C library can be loaded by passing None. 36 | libfib = ffi.dlopen(os.path.join('.', 'libfibonacci.so')) 37 | 38 | fib_py = fib_python.compute_fibonacci(n) 39 | fib_cffi = libfib.compute_fibonacci(n) 40 | if fib_py != fib_cffi: 41 | raise (ValueError(fib_cffi)) 42 | 43 | py_tot = timeit.timeit("compute_fibonacci({})".format(n), 44 | setup="from fib_python import compute_fibonacci", 45 | number=number_of_times) 46 | cffi_tot = timeit.timeit("libfib.compute_fibonacci({})".format(n), 47 | setup="""import cffi; ffi = cffi.FFI(); ffi.cdef('int compute_fibonacci(int n);'); libfib = ffi.dlopen('./libfibonacci.so')""", 48 | number=number_of_times) 49 | py_avg = py_tot / number_of_times 50 | cffi_avg = cffi_tot / number_of_times 51 | 52 | print("fib({}) = {}".format(n, fib_py)) 53 | print("Python average time: {0:.2g}".format(py_avg)) 54 | print("CFFI/C average time: {0:.2g}".format(cffi_avg)) 55 | print("CFFI/C speedup: {0:.2g} times".format(py_avg / cffi_avg)) 56 | -------------------------------------------------------------------------------- /swig/structs/fibonacci.c: -------------------------------------------------------------------------------- 1 | /* File : fibonacci.c 2 | * 3 | * Implementation of the algorithm in C 4 | */ 5 | #include "fibonacci.h" 6 | 7 | #include // printf() 8 | #include // atoi() 9 | #include // clock_t, clock() 10 | 11 | int compute_fibonacci(int n) 12 | { 13 | int temp; 14 | int a = 1; 15 | int b = 1; 16 | for (int x=0; x 1) 39 | { 40 | n = atoi(argv[1]); 41 | } 42 | 43 | int number_of_times = 100000; 44 | if (argc > 2) 45 | { 46 | number_of_times = atoi(argv[2]); 47 | } 48 | 49 | int fib_n = compute_fibonacci(n); 50 | 51 | // Compute single execution time 52 | clock_t begin = clock(); 53 | compute_fibonacci(n); 54 | clock_t end = clock(); 55 | double single_time = (double)(end - begin) / CLOCKS_PER_SEC; 56 | printf("single execution time: %.2g s\n", single_time); 57 | 58 | // Compute average execution time over many times 59 | double time_spent = 0; 60 | for (int i = 0; i < number_of_times; i++) 61 | { 62 | begin = clock(); 63 | compute_fibonacci(n); 64 | end = clock(); 65 | time_spent += (double)(end - begin) / CLOCKS_PER_SEC; 66 | } 67 | double avg_time = time_spent / number_of_times; 68 | 69 | printf("fib(%d) = %d [average execution time: %.2g s]\n", n, fib_n, avg_time); 70 | 71 | 72 | // Create and populate a struct 73 | struct MsgMetaData myStruct = populate_struct(1, 2, 3); 74 | 75 | printf("msg type = %d, job id = %d, msg size = %d\n", myStruct.type, myStruct.job, 76 | myStruct.size); 77 | 78 | return EXIT_SUCCESS; 79 | } 80 | -------------------------------------------------------------------------------- /swig/fastlz/Compress.cpp: -------------------------------------------------------------------------------- 1 | #include "Compress.h" 2 | 3 | #include 4 | #include "fastlz.h" 5 | 6 | // Intermediate private helper function which wraps fastlz function for compression 7 | static bool _compress(const void *buffer, uint32_t inSize, void *bufOut, uint32_t &sizeOut) 8 | { 9 | // Has a minimum input buffer size of 16 bytes 10 | if (inSize < 16) 11 | { 12 | return false; 13 | } 14 | 15 | int ret = fastlz_compress(buffer, inSize, bufOut); 16 | sizeOut = ret; 17 | if (0 != ret) 18 | { 19 | return true; 20 | } 21 | 22 | return false; 23 | } 24 | 25 | static bool _decompress(const void *buffer, uint32_t inSize, void *bufOut, uint32_t &sizeOut) 26 | { 27 | int ret = fastlz_decompress(buffer, inSize, bufOut, sizeOut); 28 | sizeOut = ret; 29 | if (0 != ret) 30 | { 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | 38 | // Public functions 39 | 40 | bool Compress(const std::vector& in, std::vector& out) 41 | { 42 | size_t N = in.size(); 43 | 44 | // Compressed buffer should be *at least* 5% larger than the input buffer 45 | // We'll err on the side of caution and make it 50% larger 46 | size_t M = 1.5 * N; 47 | 48 | // Compressed buffer needs to have a minimum size of 66 bytes 49 | if (M < 66) 50 | { 51 | M = 66; 52 | } 53 | 54 | // If output vector isn't big enough, resize it 55 | if (out.size() < M) 56 | { 57 | out.resize(M); 58 | } 59 | 60 | // Call file-scope _compress() wrapper around the fastlz function 61 | bool ok; 62 | uint32_t compLen = 0; 63 | ok = _compress(in.data(), N, out.data(), compLen); 64 | if (ok) 65 | { 66 | // Compression succeeded, resize the output vector to correct length 67 | out.resize(compLen); 68 | } 69 | 70 | return ok; 71 | } 72 | 73 | bool Decompress(const std::vector& in, std::vector& out) 74 | { 75 | // Call the file-scope _decompress() wrapper around the fastlz function 76 | bool ok; 77 | uint32_t uncLen = out.size(); 78 | ok = _decompress(in.data(), in.size(), out.data(), uncLen); 79 | if (ok) 80 | { 81 | // Decompression succeeded, resize the output vector to correct length 82 | out.resize(uncLen); 83 | } 84 | 85 | return ok; 86 | } 87 | -------------------------------------------------------------------------------- /cython/wrap_arrays/test_cython_wrapper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import cyfastlz 3 | 4 | def test_compress_and_decompress_roundtrip(): 5 | # Beginning of the United States Constitution 6 | text ="""We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defence, promote the general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do ordain and establish this Constitution for the United States of America. 7 | 8 | Article I (Article 1 - Legislative) 9 | Section 1 10 | All legislative Powers herein granted shall be vested in a Congress of the United States, which shall consist of a Senate and House of Representatives. 11 | 12 | Section 2 13 | 1: The House of Representatives shall be composed of Members chosen every second Year by the People of the several States, and the Electors in each State shall have the Qualifications requisite for Electors of the most numerous Branch of the State Legislature. 14 | 15 | 2: No Person shall be a Representative who shall not have attained to the Age of twenty five Years, and been seven Years a Citizen of the United States, and who shall not, when elected, be an Inhabitant of that State in which he shall be chosen. 16 | 17 | 3: Representatives and direct Taxes shall be apportioned among the several States which may be included within this Union, according to their respective Numbers, which shall be determined by adding to the whole Number of free Persons, including those bound to Service for a Term of Years, and excluding Indians not taxed, three fifths of all other Persons.2 The actual Enumeration shall be made within three Years after the first Meeting of the Congress of the United States, and within every subsequent Term of ten Years, in such Manner as they shall by Law direct. The Number of Representatives shall not exceed one for every thirty Thousand, but each State shall have at Least one Representative; and until such enumeration shall be made, the State of New Hampshire shall be entitled to chuse three, Massachusetts eight, Rhode-Island and Providence Plantations one, Connecticut five, New-York six, New Jersey four, Pennsylvania eight, Delaware one, Maryland six, Virginia ten, North Carolina five, South Carolina five, and Georgia three. 18 | 19 | 4: When vacancies happen in the Representation from any State, the Executive Authority thereof shall issue Writs of Election to fill such Vacancies. 20 | 21 | 5: The House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of Impeachment.""" 22 | 23 | # Convert the ntext to a bytes 24 | text_bytes = text.encode() 25 | 26 | # Compress a known text 27 | compressed = cyfastlz.compress(text_bytes) 28 | 29 | # Verify that the compressed text is actually smaller than the original 30 | assert len(compressed) < len(text_bytes) 31 | 32 | # Decompress the compressed text to reconstruct the original as a a bytes 33 | reconstructed_bytes = cyfastlz.decompress(compressed) 34 | 35 | # Convert back to a Python 3 unicode string 36 | reconstructed = reconstructed_bytes.decode() 37 | 38 | # Verify the reconstructed text is the same as the original 39 | assert text == reconstructed 40 | -------------------------------------------------------------------------------- /swig/fastlz/test_swig.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | """ pytest unit test for the SWIG wrapper for compressing and decompressing 4 | using the fastlz C library. 5 | """ 6 | import numpy as np 7 | from Compress import VectorUint8, Compress, Decompress 8 | 9 | 10 | def test_compress_and_decompress_roundtrip(): 11 | # Beginning of the United States Constitution 12 | text ="""We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defence, promote the general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do ordain and establish this Constitution for the United States of America. 13 | 14 | Article I (Article 1 - Legislative) 15 | Section 1 16 | All legislative Powers herein granted shall be vested in a Congress of the United States, which shall consist of a Senate and House of Representatives. 17 | 18 | Section 2 19 | 1: The House of Representatives shall be composed of Members chosen every second Year by the People of the several States, and the Electors in each State shall have the Qualifications requisite for Electors of the most numerous Branch of the State Legislature. 20 | 21 | 2: No Person shall be a Representative who shall not have attained to the Age of twenty five Years, and been seven Years a Citizen of the United States, and who shall not, when elected, be an Inhabitant of that State in which he shall be chosen. 22 | 23 | 3: Representatives and direct Taxes shall be apportioned among the several States which may be included within this Union, according to their respective Numbers, which shall be determined by adding to the whole Number of free Persons, including those bound to Service for a Term of Years, and excluding Indians not taxed, three fifths of all other Persons.2 The actual Enumeration shall be made within three Years after the first Meeting of the Congress of the United States, and within every subsequent Term of ten Years, in such Manner as they shall by Law direct. The Number of Representatives shall not exceed one for every thirty Thousand, but each State shall have at Least one Representative; and until such enumeration shall be made, the State of New Hampshire shall be entitled to chuse three, Massachusetts eight, Rhode-Island and Providence Plantations one, Connecticut five, New-York six, New Jersey four, Pennsylvania eight, Delaware one, Maryland six, Virginia ten, North Carolina five, South Carolina five, and Georgia three. 24 | 25 | 4: When vacancies happen in the Representation from any State, the Executive Authority thereof shall issue Writs of Election to fill such Vacancies. 26 | 27 | 5: The House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of Impeachment.""" 28 | 29 | # TODO: Convert the text to a VectorUint8 which can be passed to Compress 30 | 31 | # TODO: Create a vector to store the compressed data 32 | 33 | # Compress the input text 34 | success = Compress(text_vec, compressed_vec) 35 | assert success 36 | 37 | # Verify that the compressed text is actually smaller than the original 38 | assert len(compressed_vec) < len(text_vec) 39 | 40 | # TODO: Create a vector for the reconstructed text 41 | 42 | # Decmopress the compressed text to reconstruct a vector of original bytes 43 | success = Decompress(compressed_vec, recon_vec) 44 | assert success 45 | 46 | # TODO: Convert the reconstructed text to a bytes 47 | 48 | # TODO: And finally back to a str 49 | 50 | # Verify the reconstructed text is the same as the original 51 | assert text == reconstructed 52 | -------------------------------------------------------------------------------- /swig/fastlz/fastlz.h: -------------------------------------------------------------------------------- 1 | /* 2 | FastLZ - lightning-fast lossless compression library 3 | 4 | Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) 5 | Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) 6 | Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | #ifndef FASTLZ_H 27 | #define FASTLZ_H 28 | 29 | #define FASTLZ_VERSION 0x000100 30 | 31 | #define FASTLZ_VERSION_MAJOR 0 32 | #define FASTLZ_VERSION_MINOR 0 33 | #define FASTLZ_VERSION_REVISION 0 34 | 35 | #define FASTLZ_VERSION_STRING "0.1.0" 36 | 37 | #if defined (__cplusplus) 38 | extern "C" { 39 | #endif 40 | 41 | /** 42 | Compress a block of data in the input buffer and returns the size of 43 | compressed block. The size of input buffer is specified by length. The 44 | minimum input buffer size is 16. 45 | 46 | The output buffer must be at least 5% larger than the input buffer 47 | and can not be smaller than 66 bytes. 48 | 49 | If the input is not compressible, the return value might be larger than 50 | length (input buffer size). 51 | 52 | The input buffer and the output buffer can not overlap. 53 | */ 54 | 55 | int fastlz_compress(const void* input, int length, void* output); 56 | 57 | /** 58 | Decompress a block of compressed data and returns the size of the 59 | decompressed block. If error occurs, e.g. the compressed data is 60 | corrupted or the output buffer is not large enough, then 0 (zero) 61 | will be returned instead. 62 | 63 | The input buffer and the output buffer can not overlap. 64 | 65 | Decompression is memory safe and guaranteed not to write the output buffer 66 | more than what is specified in maxout. 67 | */ 68 | 69 | int fastlz_decompress(const void* input, int length, void* output, int maxout); 70 | 71 | /** 72 | Compress a block of data in the input buffer and returns the size of 73 | compressed block. The size of input buffer is specified by length. The 74 | minimum input buffer size is 16. 75 | 76 | The output buffer must be at least 5% larger than the input buffer 77 | and can not be smaller than 66 bytes. 78 | 79 | If the input is not compressible, the return value might be larger than 80 | length (input buffer size). 81 | 82 | The input buffer and the output buffer can not overlap. 83 | 84 | Compression level can be specified in parameter level. At the moment, 85 | only level 1 and level 2 are supported. 86 | Level 1 is the fastest compression and generally useful for short data. 87 | Level 2 is slightly slower but it gives better compression ratio. 88 | 89 | Note that the compressed data, regardless of the level, can always be 90 | decompressed using the function fastlz_decompress above. 91 | */ 92 | 93 | int fastlz_compress_level(int level, const void* input, int length, void* output); 94 | 95 | #if defined (__cplusplus) 96 | } 97 | #endif 98 | 99 | #endif /* FASTLZ_H */ 100 | -------------------------------------------------------------------------------- /cython/wrap_arrays/fastlz.h: -------------------------------------------------------------------------------- 1 | /* 2 | FastLZ - lightning-fast lossless compression library 3 | 4 | Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) 5 | Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) 6 | Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | #include 27 | 28 | #ifndef FASTLZ_H 29 | #define FASTLZ_H 30 | 31 | #define FASTLZ_VERSION 0x000100 32 | 33 | #define FASTLZ_VERSION_MAJOR 0 34 | #define FASTLZ_VERSION_MINOR 0 35 | #define FASTLZ_VERSION_REVISION 0 36 | 37 | #define FASTLZ_VERSION_STRING "0.1.0" 38 | 39 | #if defined (__cplusplus) 40 | extern "C" { 41 | #endif 42 | 43 | /** 44 | Compress a block of data in the input buffer and returns the size of 45 | compressed block. The size of input buffer is specified by length. The 46 | minimum input buffer size is 16. 47 | 48 | The output buffer must be at least 5% larger than the input buffer 49 | and can not be smaller than 66 bytes. 50 | 51 | If the input is not compressible, the return value might be larger than 52 | length (input buffer size). 53 | 54 | The input buffer and the output buffer can not overlap. 55 | */ 56 | 57 | int fastlz_compress(const uint8_t* input, int length, uint8_t* output); 58 | 59 | /** 60 | Decompress a block of compressed data and returns the size of the 61 | decompressed block. If error occurs, e.g. the compressed data is 62 | corrupted or the output buffer is not large enough, then 0 (zero) 63 | will be returned instead. 64 | 65 | The input buffer and the output buffer can not overlap. 66 | 67 | Decompression is memory safe and guaranteed not to write the output buffer 68 | more than what is specified in maxout. 69 | */ 70 | 71 | int fastlz_decompress(const uint8_t* input, int length, uint8_t* output, int maxout); 72 | 73 | /** 74 | Compress a block of data in the input buffer and returns the size of 75 | compressed block. The size of input buffer is specified by length. The 76 | minimum input buffer size is 16. 77 | 78 | The output buffer must be at least 5% larger than the input buffer 79 | and can not be smaller than 66 bytes. 80 | 81 | If the input is not compressible, the return value might be larger than 82 | length (input buffer size). 83 | 84 | The input buffer and the output buffer can not overlap. 85 | 86 | Compression level can be specified in parameter level. At the moment, 87 | only level 1 and level 2 are supported. 88 | Level 1 is the fastest compression and generally useful for short data. 89 | Level 2 is slightly slower but it gives better compression ratio. 90 | 91 | Note that the compressed data, regardless of the level, can always be 92 | decompressed using the function fastlz_decompress above. 93 | */ 94 | 95 | int fastlz_compress_level(int level, const uint8_t* input, int length, uint8_t* output); 96 | 97 | #if defined (__cplusplus) 98 | } 99 | #endif 100 | 101 | #endif /* FASTLZ_H */ 102 | -------------------------------------------------------------------------------- /Prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites for Running Example Code 2 | The examples on this site are compatible with Mac OS X, Linux, and Windows. To run these examples 3 | you will need various C/C++ and Python software installed. The instructions here cover the 4 | prerequisites necessary to run the Cython, SWIG, and CFFI examples. 5 | 6 | 7 | ## C and C++ compiler toolchains 8 | To run most of the examples you need a C compiler toolchain and for some of them you also need 9 | a C++ compiler toolchain. How to get this installed depends on which operating system (OS) you 10 | are running. 11 | 12 | ### Linux 13 | If you are using any of Linux OSes which use **apt** for a package manager (Debian, Ubuntu, or Mint), 14 | then you can do the following to get all of the C and C++ tools you need: 15 | 16 | ```bash 17 | sudo apt-get install build-essential 18 | ``` 19 | 20 | For other Linux package managers, the fundamental tools you want to install are ``gcc`` and ``g++``. 21 | 22 | ### Mac OS X 23 | Install the free **XCode** developer tools from the App Store. Make sure you run it at least once and 24 | say "yes" if it asks you if you want to install the command-line tools. 25 | 26 | ### Windows 27 | There are a number of options for Windows, but by far the simplest is to use the same compiler which 28 | Python itself was compiled with. For recent versions of Python released within the past couple years, 29 | this is the Visual C++ compiler which comes with Visual Studio 2017. 30 | 31 | It is relatively easy to use both SWIG and Cython on Windows because all C/C++ compilation can 32 | coordinated within the setup.py file and handled either by the setuptools or distutils Python module. 33 | In this case, Python will automatically know where all of the correct libraries to link against are. 34 | 35 | Using CFFI can be a little bit more finicky since it is directly calls into pre-compiled dynamic 36 | libraries and makes some assumptions regarding the C ABI. So you really need to make sure you are 37 | using the exact compiler that Python itself was compiled with. 38 | 39 | See the [WindowsCompliers](https://wiki.python.org/moin/WindowsCompilers) section of the Python Wiki for more 40 | information. 41 | 42 | **If you try to use the examples on Windows and run into difficulties, you may wish to setup a Linux 43 | virtual machine (VM) for evaluation purposes since C/C++ compilers are a lot easier to setup on Linux. 44 | We recommend using 45 | [VMware Player](http://www.vmware.com/products/player/playerpro-evaluation.html) or 46 | [Virtualbox](https://www.virtualbox.org) to install a Linux Virtual Machine (VM).** 47 | 48 | 49 | ## Python and Other Tools 50 | 51 | ### Pipenv 52 | [Pipenv](https://github.com/pypa/pipenv) is an easy way to install compatible versions of `cffi`, `cython`, and 53 | `pybind11`. Once `pipenv` in installed, these other packages can be installed in a virtual environment using: 54 | ```shell script 55 | pipenv install 56 | ``` 57 | 58 | Then this Python virtual environment can be entered using: 59 | ```shell script 60 | pipenv shell 61 | ``` 62 | 63 | ### Anaconda Python 64 | Another easy way to get all of the tools installed and setup is by using the Anaconda Python 65 | distribution. This is a free distribution of Python available for Windows, Mac OS X, and Linux which 66 | comes with a couple hundred of the most common and useful Python modules pre-installed including Cython 67 | and CFFI. It also comes with the ``conda`` package manager which can be used to install additional 68 | tools such as SWIG. 69 | 70 | Download the latest version of [Anaconda](https://www.continuum.io/downloads). We recommend Python 3.6 or newer. 71 | 72 | Install Anaconda per the instructions and let it modify your .bashrc, .bash_profile, or path accordingly. 73 | NOTE: Do not use ``sudo`` to install Anaconda, just install it as a normal user. 74 | 75 | This also installs Cython and CFFI. 76 | 77 | ## SWIG 78 | If Anaconda is installed, you can use the included **conda** package manager to install SWIG. Do 79 | the following in a command-line terminal: 80 | 81 | ```bash 82 | conda install swig 83 | ``` 84 | 85 | Alternatively, on macOS you can use the Homebrew package manager to install SWIG: 86 | 87 | ```shell script 88 | brew install swig 89 | ``` 90 | 91 | ## PyPy 92 | PyPy is best installed with a package manager such as *brew* on Mac OS X or *apt-get** on many Linux distros. 93 | 94 | ### PyPy on Mac 95 | Use the Homebrew package manager to install 96 | 97 | ```bash 98 | brew install pypy 99 | ``` 100 | 101 | ### PyPy on Linux 102 | Use your the package manager which comes your distro to install pypy. For example, on Debian, Ubuntu, or Mint 103 | distros: 104 | 105 | ```bash 106 | sudo apt-get install pypy 107 | ``` 108 | 109 | ### PyPy on Windows 110 | Download a Windows binary from the PyPy website: http://pypy.org/download.html 111 | -------------------------------------------------------------------------------- /cython/integrate/Readme.md: -------------------------------------------------------------------------------- 1 | # Faster code via static typing and Cython 2 | Cython is a Python compiler. This means that it can compile normal Python code without changes (with a few 3 | obvious exceptions of some as-yet unsupported language features). However, for performance critical code, 4 | it is often helpful to add static type declarations, as they will allow Cython to step out of the dynamic 5 | nature of the Python code and generate simpler and faster C code - sometimes faster by orders of magnitude. 6 | 7 | It must be noted, however, that type declarations can make the source code more verbose and thus less 8 | readable. It is therefore discouraged to use them without good reason, such as where benchmarks prove that 9 | they really make the code substantially faster in a performance critical section. Typically a few types in 10 | the right spots go a long way. 11 | 12 | All C types are available for type declarations: integer and floating point types, complex numbers, structs, 13 | unions and pointer types. Cython can automatically and correctly convert between the types on assignment. 14 | This also includes Python’s arbitrary size integer types, where value overflows on conversion to a C type 15 | will raise a Python OverflowError at runtime. (It does not, however, check for overflow when doing 16 | arithmetic.) The generated C code will handle the platform dependent sizes of C types correctly and safely 17 | in this case. 18 | 19 | Types are declared via the cdef keyword. 20 | 21 | ## Exercise 22 | 23 | The file **integrate.py** contains pure Python code for numerically integrating a function. The file 24 | **cyintegrate.pyx** contains an exact copy of the code in *integrate.py*. The **build_cython.sh** script 25 | builds the Cython code present in **cyintegrate.pyx** using the configuration in **setup.py**. 26 | 27 | The **cython_speedup.py** file is setup to import both the pure Python and the Cython version and to 28 | benchmark their performance using a common configuration. 29 | 30 | NOTE: You need to rebuild your Cython code anytime you make changes to **cyintegrate.pyx** 31 | 32 | 33 | ## Step 0 - rename the .py file to .pyx 34 | The first thing you need to do when using Cython to optimize existing Python code is to rename the 35 | module you want to optimize to have a **.pyx** extension instead of **.py**. Then you need to create 36 | a **setup.py** file which uses the **setuptools** module to Cythonize and compile your code. These 37 | preliminary steps have both already been done for you in this exercise. 38 | 39 | ## Step 1 - see how things work to start with 40 | Cython will give some performance benefit even when compiling Python code without any static type 41 | declarations. 42 | 43 | Build the Cython code: 44 | 45 | ```bash 46 | python setup.py build_ext --inplace 47 | ``` 48 | 49 | Run **cython_speedup.py** to compare the two implementations at this point. 50 | 51 | ```bash 52 | python cython_speedup.py 53 | ``` 54 | 55 | On my system, even though we have done **nothing** to change the pure Python code in any way, Cython 56 | provides about a 60% speedup. 57 | 58 | 59 | ## Step 2 - look at the HTML annotation file 60 | The **setup.py** file contains this code which tells Cython to create a *.html annotations file: 61 | 62 | ```python 63 | import Cython.Compiler.Options 64 | Cython.Compiler.Options.annotate = True 65 | ``` 66 | 67 | This is equivalent to calling cythonize with the **-a** flag at the command line. 68 | 69 | Open the **cyintegrate.html** file in the web browser of your choice. Yellow lines show code which requires 70 | interaction with the Python interpreter. The darker the yellow, the more interactions with the Python 71 | interpreter. Any interactions with the Python interpreter slow Cython's generated C/C++ code down. White lines 72 | indicate no interaction with the Python interpreter (pure C code). 73 | 74 | What you want to do is get rid of as much yellow as possible and end up with as much white as possible. This 75 | matters particularly inside loops. The main way you get rid of these interactions with the Python interpreter 76 | is to declare optional C static types, so Cython can use them to generate fast C code. 77 | 78 | After each optimization step in this exercise, you should re-examine the HTML annotation file and let 79 | the remaining yellow lines guide your next optimization step. 80 | 81 | 82 | ## Step 3 - Typing Variables 83 | As we saw, simply compiling this code in Cython merely gives a 60% speedup. This is better than nothing, 84 | but adding some static types can make a much larger difference. 85 | 86 | Types for function arguments can be added simply by prefacing the parameter name with a C type, such as: 87 | 88 | ```python 89 | def f(double x): 90 | return cos(x) 91 | ``` 92 | 93 | Types for local variables can be added by declaring them wtih **cdef**: 94 | ```python 95 | cdef int i 96 | ``` 97 | 98 | Try adding static types for all function arguments and all local variables. I recommend using **double** for 99 | floating point types since the Python **float** type corresponds to a C **double**. Once you have done 100 | this, recompile your Cython code and re-run **cython_speedup.py**. 101 | 102 | This results in a 2.5 times speedup over the pure Python version. 103 | 104 | 105 | ## Step 4 - Typing Functions 106 | Python function calls can be expensive – in Cython doubly so because one might need to convert to and from 107 | Python objects to do the call. In our example above, the argument is assumed to be a C double both inside 108 | f() and in the call to it, yet a Python float object must be constructed around the argument in order to 109 | pass it. 110 | 111 | Therefore Cython provides a syntax for declaring a C-style function, the cdef keyword: 112 | ```python 113 | cdef double f(double x) except? -2: 114 | return cos(x) 115 | ``` 116 | 117 | Some form of except-modifier should usually be added, otherwise Cython will not be able to propagate 118 | exceptions raised in the function (or a function it calls). The except? -2 means that an error will be 119 | checked for if -2 is returned (though the ? indicates that -2 may also be used as a valid return value). 120 | Alternatively, the slower except * is always safe. An except clause can be left out if the function returns 121 | a Python object or if it is guaranteed that an exception will not be raised within the function call. 122 | 123 | A side-effect of cdef is that the function is no longer available from Python-space, as Python wouldn’t 124 | know how to call it. It is also no longer possible to change f() at runtime. 125 | 126 | Using the cpdef keyword instead of cdef, a Python wrapper is also created, so that the function is 127 | available both from Cython (fast, passing typed values directly) and from Python (wrapping values in Python 128 | objects). In fact, cpdef does not just provide a Python wrapper, it also installs logic to allow the 129 | method to be overridden by python methods, even when called from within cython. This does add a tiny 130 | overhead compared to cdef methods. 131 | 132 | Speedup: 5 times over pure Python. 133 | 134 | 135 | ## Step 5 - Replacing Python standard library calls with C library calls 136 | The call to **cos(x)** still requires interaction with the Python interpreter. The C standard library 137 | also has a pure C implementation of the cosine function. Wouldn't it be great if we could just call that 138 | function instead? 139 | 140 | Well, we can! Cython supports this sort of syntax: 141 | 142 | ```python 143 | from libc.math cimport cos 144 | ``` 145 | 146 | We can replace the "from math import cos" line with the above for further performance improvements. 147 | 148 | Speedup: 28 times over pure Python. 149 | 150 | 151 | ## Step 6 - Tell Cython to disable unnecessary safety checks 152 | Python has a bunch of built in "safety" checks which aren't present in C. It checks for things like 153 | division by zero, accessing an array out of bounds, integer overflow, etc. Unless you tell Cython you 154 | don't want it to do this sort of checking, it will continue to do it just like Python. 155 | 156 | If you know your code is safe and Cython doesn't need to do these sorts of checks, you can inform Cython 157 | of this to get some extra performance boost. 158 | 159 | In our example, there is one last nagging line of yellow, which if we expand it is due to a floating point 160 | division check. Since this isn't in the loop, disabling this check won't buy us much here, but if it 161 | were in the loop it would make a difference. Regardless, it is good to know how to tell Cython to do this 162 | sort of thing. 163 | 164 | So to disable the floating point division check for a particular function, you can do this: 165 | 166 | ```python 167 | import cython 168 | 169 | @cython.cdivision(True) 170 | cpdef double integrate_f(double a, double b, int N): 171 | ... 172 | ``` 173 | 174 | Look here for more info: [Cython compiler directives](http://cython.readthedocs.io/en/latest/src/reference/compilation.html#compiler-directives) 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python_Interface_Cpp 2 | Example code for interfacing with C and C++ from Python using Cython, SWIG, PyPy, CFFI, and pybind11: 3 | 4 | * [Cython](http://cython.org) 5 | * [SWIG](http://www.swig.org) 6 | * [PyPy](https://pypy.org) 7 | * [CFFI](http://cffi.readthedocs.io) 8 | * [pybind11](https://github.com/pybind/pybind11) 9 | 10 | ## Fibonacci Example 11 | This repository has example code for interfacing with some C code for calculating the Nth 12 | fibonacci number from Python using the four different tools. 13 | 14 | This is the one universal example present for all tools and is thus intended to provide 15 | an apples-to-apples comparison both in terms of API/usage and performance. 16 | 17 | ### Fibonacci Performance Benchmarks 18 | One metric for evaluating these tools is performance. Here is a table which shows speedup 19 | factor relative to the pure Python implementation of the Fibonacci code. So a speedup of **2** 20 | means that code ran twice as fast as the pure Python version. 21 | 22 | | Tool | Speedup | 23 | | ------------------ | -------:| 24 | | pypy + CFFI | 40 | 25 | | Cython (optimized) | 27 | 26 | | Cython (wrapper) | 25 | 27 | | SWIG | 14 | 28 | | pybind11 | 10 | 29 | | pypy | 8 | 30 | | CFFI | 6 | 31 | | Python | 1 | 32 | 33 | NOTE: These numbers were originally measured on a 2013 15" Mac Book Pro using Python 3.6 via Anaconda distro 34 | with the latest versions of all tools installed using the conda package manager. I have more recently measured 35 | using a Python 3.7 virtual environment via [Pipenv](https://github.com/pypa/pipenv) and confirmed that they 36 | stayed approximately the same and relative rankings in terms of performance are identical. 37 | 38 | The Fibonacci example presented here is pretty trivial. Your mileage may vary depending on your 39 | application. But overall these performance measurements are fairly representative of what you 40 | may typically expect from each of these tools. 41 | 42 | In general whenever you see nested loops in your Python code, that is where you should expect the 43 | most speedup can be gained by optimizing with C/C++. Speedups on the order of 1000 are not 44 | uncommon if you are dealing with triply-nested loops for things like image processing code. 45 | 46 | ## Other Examples 47 | Some of the tools have other examples provided as well, typically for more advanced features. 48 | But no attempt is made to provide an apples-to-apples cross-tool comparison for these other 49 | examples. 50 | 51 | # Meet the tools 52 | 53 | ## Cython 54 | Cython is the C-Extensions for Python module and it is an optimising static transpiler which 55 | lets you use optional C static types embedded in your Python code and automatically translate 56 | that to C or C++ code and then automatically compile it. 57 | 58 | But all of that happens pretty much behind the scenes, and you are left with code that looks 59 | mostly like Python, but has the performance of C. 60 | 61 | It has a bit of a learning curve, but it is amazing. In particular it is awesome for 62 | optimizing existing Python code. But it is also good for providing really high performance 63 | wrappers of existing C/C++ code with a clean Pythonic API. It just requires you to manually 64 | do the wrapping, so it involves significantly more work on your part to do so than using SWIG. 65 | 66 | The [Cython documentation](http://docs.cython.org/en/latest/) is excellent and there is a great 67 | [book](https://www.amazon.com/Cython-Programmers-Kurt-W-Smith/dp/1491901551) from OReilly and also some great 68 | video presentations on [YouTube](https://www.youtube.com/watch?v=gMvkiQ-gOW8&t=14s) and on [Safari](http://shop.oreilly.com/product/0636920046813.do). 69 | 70 | ## SWIG 71 | SWIG is the Simplified Wrapper and Interface Generator. It is capable of wrapping C and C++ 72 | code/libraries in about 20 different target languages including Python, Java, C#, etc. 73 | 74 | SWIG largely automates the process of wrapping existing C/C++ code so it is faster and easier 75 | to use for that purpose than Cython. 76 | 77 | The [SWIG documentation](http://www.swig.org/Doc3.0/index.html) can sometimes leave something to be desired, but it is an extremely powerful 78 | tool which is an excellent choice in many situations. A professor from the University of Oslo has 79 | a decent [video tutorial](https://www.youtube.com/watch?v=J-iVTLp6M9I) with [example code](https://github.com/UiO-INF3331/code-snippets/tree/master/mixed/swig) on GitHub. 80 | 81 | SWIG is the granddaddy of all of the tools here. It has been around for a long time. Given how 82 | long it has been around and the wealth of target languages, it is probably the most widely used 83 | of these tools. 84 | 85 | ## PyPy 86 | PyPy is a fast, compliant alternative implementation of the Python language (2.7.13 and 3.5.3) in Python. 87 | The normal implementation of Python is in C and is referred to as CPython. PyPy uses a Just-in-Time (JIT) 88 | compiler, so Python programs often run significantly faster on PyPy. It also employs various memory 89 | optimizations compared to CPython. 90 | 91 | The major advantage of PyPy is that it can deliver some quick and easy wins. You don't need to change 92 | your existing Python code in any way! You just install PyPy and run your Python program using **pypy** 93 | instead of **python**. 94 | 95 | The major disadvantage of PyPy is that not all 3rd-party Python libraries work with PyPy. Detailed 96 | information on which modules are compatible can be found [here](https://pypy.org/compat.html). 97 | 98 | ## CFFI 99 | CFFI is the C Foreign Function Interface for Python. It is basically an improved version of 100 | Python's built-in ctypes. 101 | 102 | It can call functions in a C dynamic library just by writing code inline in Python. There 103 | isn't any sort of need for an external wrapper. Hence, it is the quickest and easiest 104 | of these libraries to use to interface with existing C dynamic libraries. However, the 105 | performance is decidedly worse than the other options presented here unless it is used in combination 106 | with PyPy, in which case the performance is truly excellent. 107 | 108 | Its real strength lies in 100% compatibility with PyPy and lower overhead present when using CFFI 109 | with PyPy's JIT. So it is the go-to choice if you are 110 | using the PyPy JIT. That being said, I don't think I would recommend using it if you aren't using 111 | PyPy because either Cython or SWIG tend to be a better fit for most applications when used with 112 | the normal CPython implementation of Python. 113 | 114 | CFFI has decent [documentation](https://cffi.readthedocs.io/en/latest/) and here is a [tutorial video](https://www.youtube.com/watch?v=ThDFmuXH15k). 115 | 116 | ## pybind11 117 | pybind11 is essentially what arose from the ashes of Boost.Python. It is the newest of the tools 118 | presented here, but it is already better than 119 | Boost.Python ever was. 120 | 121 | It only works with bleeding-edge C++11 compilers. My experience is that I couldn't get it to work on Mac OS X at all and I tried with both Python 3.6 and Python 2.7, both from Anaconda distro and using default LLVM compiler from Xcode on Mac OS X 122 | 10.12.4. I also could not get it working on either Ubuntu 16.04 or 14.04 with either Python 2.7 or 3.6. I was able to get it working on Debian 9 with both Python 2.7 and 3.5 as installed from apt-get. Given that experience I wouldn't even consider it remotely stable yet. 123 | 124 | The performance is worse than either SWIG or Cython and the ease of use is not as easy to use as SWIG. 125 | So does a niche exist for pybind11? pybind11 does make it easy to embedded Python within a C++ project and it is well 126 | supported on Windows. Here is a [good video](https://channel9.msdn.com/Shows/Visual-Studio-Toolbox/Embedding-Python-in-a-C-Project) that demonstrates scripting a 127 | C++ application with Python using pybind11 and Visual Studio 2017. 128 | 129 | [pybind11 documentation](http://pybind11.readthedocs.io/en/stable/) is decent and here is a [conference video](https://www.youtube.com/watch?v=jQedHfF1Jfw). 130 | 131 | pybind11 does appear to be under rapid development, so maybe it will get better in the future; but at this time I can 132 | only recommend it for embedding Python within a C++ project (though it is probably worth noting that Cython is also 133 | capable of doing that). 134 | 135 | # Conclusion / Recommendations 136 | Ok, here are some of my thoughts. They are my opinions and are hence subjective in nature, though 137 | they are grounded in my having actually evaluated these tools at length. 138 | 139 | If what you care about most is performance, then Cython is the clear winner by a wide margin. But on 140 | the flip side, this tool has the largest learning curve, at least if you are looking at wrapping 141 | existing C++ code. 142 | 143 | If what you care about most is productivity, then SWIG is the clear winner if you want to wrap existing 144 | C/C++ code and Cython is the clear winner if you want to optimize existing Python code. 145 | 146 | If all of the 3rd-party Python libraries you are using are compatible with PyPy, then using PyPy can 147 | provide a painless performance boost for free. Given that there are no code changes required to use it, 148 | it is very much worth trying it and seeing what sort of speedup you get. 149 | 150 | CFFI is pretty lame unless you are using PyPy. But it is super easy to use with near zero learning curve. 151 | So if you just want to call a function or two from an existing C library, it may be your best bet. On 152 | the other hand, if all of the 3rd party libraries you are using work with PyPy and you only want to 153 | interface with C code (not C++), then the combination of PyPy + CFFI can result in some truly impressive 154 | performance improvements. 155 | 156 | pybind11 struggles with easy cross-platform compatibility and its performance is worse than SWIG, but it is more of a 157 | pain to use than SWIG. So I'd recommend staying away from it for now unless you are looking to embed Python code within 158 | a C++ project on Windows. 159 | 160 | ## Updates for 2019 161 | Recent re-evaluation of all these options firmly confirms my original conclusions. `Cython` and `SWIG` are both awesome 162 | with distinct tradeoffs. The combination of `PyPy` + `CFFI` is attractive and shows a lot of potential. While the 163 | cross-platform compatibility issues previously seen with `pybind11` appear to have been resolved, I still can't see a 164 | use case for which it would be superior to one of the other options; but it has a ton of stars on GitHub so perhaps I 165 | am missing something? 166 | 167 | # Running Example Code Yourself 168 | For information on getting all of the necessary prerequisites installed, see 169 | [Prerequisites for Running Example Code](https://github.com/tleonhardt/Python_Interface_Cpp/blob/master/Prerequisites.md). 170 | 171 | For info on how to build and run each particular example, see the **Readme.md** in the example directory. 172 | 173 | 174 | # Excercises 175 | There are a few examples here which are intended as guided exercises to help build your knowledge of how 176 | to use Cython and SWIG. 177 | 178 | The exercises exist on the **master** branch, while the solutions exist on the **solutions** branch. 179 | 180 | Most exercises have **TODO:** comments in the locations where they want you to write some code and have 181 | instructions in the Readme.md for that exercise. 182 | 183 | ## Cython Exercises 184 | 185 | ### Intro to Cython Exercise 186 | The [integrate](https://github.com/tleonhardt/Python_Interface_Cpp/tree/master/cython/integrate) Cython 187 | example serves as a good basic introduction to using Cython to optimize existing Python code. It 188 | details a relatively typical series of steps which are followed in using Cython for process of 189 | progressively optimizing existing Python code. 190 | 191 | ### Cython for Wrapping 192 | The [wrap_arrays](https://github.com/tleonhardt/Python_Interface_Cpp/tree/master/cython/wrap_arrays) 193 | Cython example serves as an introduction to using Cython to wrap existing C code. It purposely uses 194 | an example where the C functions take pointers to arrays, so it can help you learn how to generate 195 | wrapper code for this common type of scenario. 196 | 197 | There are better (more optimal) ways of doing this than presented in the solution. The solution tries 198 | to keep it simple. 199 | 200 | ## SWIG Exercises 201 | 202 | ### Cross-language Polymorphism in SWIG 203 | The [logger](https://github.com/tleonhardt/Python_Interface_Cpp/tree/master/swig/logger) SWIG example 204 | serves as an introduction to how to achieve true cross-language polymorphism in SWIG by using directors. 205 | This allows you to have a base class defined in C++, inherit from this class in Python, and then 206 | instantiate a C++ class which takes a pointer to the base class and you pass it a pointer to an instance 207 | of the derived class and your C++ class will end up calling virtual methods defined in Python. 208 | 209 | While this sounds complicated and abstract, it is actually pretty simple to make use of it and is of 210 | great practical utility. 211 | 212 | As a side benefit, this example also covers how to wrap STL std::string strings and effectively auto-cast 213 | them to Python str objects. 214 | 215 | ### Using STL Containers in SWIG 216 | The [fastlz](https://github.com/tleonhardt/Python_Interface_Cpp/tree/master/swig/fastlz) SWIG example 217 | serves as an introduction to how to use STL containers such as vectors in your SWIG Python wrappers. Also 218 | serves as an example of how to get your SWIG wrappers to link to dynamic libraries. 219 | 220 | ## Solutions 221 | Solutions to all exercises are in the same location as the exercise, but on the **solutions** branch. 222 | 223 | 224 | # Presentation 225 | The repository also has an overview [presentation](https://github.com/tleonhardt/Python_Interface_Cpp/tree/master/Interfacing_C_C++_with_Python.pdf) covering the basics of Cython, SWIG, and CFFI as well 226 | as when you might want to use each one. 227 | 228 | To accompany this presentation, there is a [lab guide](https://github.com/tleonhardt/Python_Interface_Cpp/tree/master/Interfacing_C_C++_with_Python-Exercises.pdf) 229 | which provides an overview of the Cython and SWIG hand-on exercises in this repository. 230 | -------------------------------------------------------------------------------- /swig/fastlz/fastlz.c: -------------------------------------------------------------------------------- 1 | /* 2 | FastLZ - lightning-fast lossless compression library 3 | 4 | Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) 5 | Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) 6 | Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | #include 27 | #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) 28 | 29 | /* 30 | * Always check for bound when decompressing. 31 | * Generally it is best to leave it defined. 32 | */ 33 | #define FASTLZ_SAFE 34 | 35 | /* 36 | * Give hints to the compiler for branch prediction optimization. 37 | */ 38 | #if defined(__GNUC__) && (__GNUC__ > 2) 39 | #define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) 40 | #define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) 41 | #else 42 | #define FASTLZ_EXPECT_CONDITIONAL(c) (c) 43 | #define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) 44 | #endif 45 | 46 | /* 47 | * Use inlined functions for supported systems. 48 | */ 49 | #if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) 50 | #define FASTLZ_INLINE inline 51 | #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) 52 | #define FASTLZ_INLINE __inline 53 | #else 54 | #define FASTLZ_INLINE 55 | #endif 56 | 57 | /* 58 | * Prevent accessing more than 8-bit at once, except on x86 architectures. 59 | */ 60 | #if !defined(FASTLZ_STRICT_ALIGN) 61 | #define FASTLZ_STRICT_ALIGN 62 | #if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ 63 | #undef FASTLZ_STRICT_ALIGN 64 | #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ 65 | #undef FASTLZ_STRICT_ALIGN 66 | #elif defined(_M_IX86) /* Intel, MSVC */ 67 | #undef FASTLZ_STRICT_ALIGN 68 | #elif defined(__386) 69 | #undef FASTLZ_STRICT_ALIGN 70 | #elif defined(_X86_) /* MinGW */ 71 | #undef FASTLZ_STRICT_ALIGN 72 | #elif defined(__I86__) /* Digital Mars */ 73 | #undef FASTLZ_STRICT_ALIGN 74 | #endif 75 | #endif 76 | 77 | /* 78 | * FIXME: use preprocessor magic to set this on different platforms! 79 | */ 80 | typedef unsigned char flzuint8; 81 | typedef unsigned short flzuint16; 82 | typedef unsigned int flzuint32; 83 | 84 | /* prototypes */ 85 | int fastlz_compress(const uint8_t* input, int length, uint8_t* output); 86 | int fastlz_compress_level(int level, const uint8_t* input, int length, uint8_t* output); 87 | int fastlz_decompress(const uint8_t* input, int length, uint8_t* output, int maxout); 88 | 89 | #define MAX_COPY 32 90 | #define MAX_LEN 264 /* 256 + 8 */ 91 | #define MAX_DISTANCE 8192 92 | 93 | #if !defined(FASTLZ_STRICT_ALIGN) 94 | #define FASTLZ_READU16(p) *((const flzuint16*)(p)) 95 | #else 96 | #define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) 97 | #endif 98 | 99 | #define HASH_LOG 13 100 | #define HASH_SIZE (1<< HASH_LOG) 101 | #define HASH_MASK (HASH_SIZE-1) 102 | #define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } 103 | 104 | #undef FASTLZ_LEVEL 105 | #define FASTLZ_LEVEL 1 106 | 107 | #undef FASTLZ_COMPRESSOR 108 | #undef FASTLZ_DECOMPRESSOR 109 | #define FASTLZ_COMPRESSOR fastlz1_compress 110 | #define FASTLZ_DECOMPRESSOR fastlz1_decompress 111 | static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const uint8_t* input, int length, uint8_t* output); 112 | static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const uint8_t* input, int length, uint8_t* output, int maxout); 113 | #include "fastlz.c" 114 | 115 | #undef FASTLZ_LEVEL 116 | #define FASTLZ_LEVEL 2 117 | 118 | #undef MAX_DISTANCE 119 | #define MAX_DISTANCE 8191 120 | #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) 121 | 122 | #undef FASTLZ_COMPRESSOR 123 | #undef FASTLZ_DECOMPRESSOR 124 | #define FASTLZ_COMPRESSOR fastlz2_compress 125 | #define FASTLZ_DECOMPRESSOR fastlz2_decompress 126 | static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const uint8_t* input, int length, uint8_t* output); 127 | static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const uint8_t* input, int length, uint8_t* output, int maxout); 128 | #include "fastlz.c" 129 | 130 | int fastlz_compress(const uint8_t* input, int length, uint8_t* output) 131 | { 132 | /* for short block, choose fastlz1 */ 133 | if(length < 65536) 134 | return fastlz1_compress(input, length, output); 135 | 136 | /* else... */ 137 | return fastlz2_compress(input, length, output); 138 | } 139 | 140 | int fastlz_decompress(const uint8_t* input, int length, uint8_t* output, int maxout) 141 | { 142 | /* magic identifier for compression level */ 143 | int level = ((*(const flzuint8*)input) >> 5) + 1; 144 | 145 | if(level == 1) 146 | return fastlz1_decompress(input, length, output, maxout); 147 | if(level == 2) 148 | return fastlz2_decompress(input, length, output, maxout); 149 | 150 | /* unknown level, trigger error */ 151 | return 0; 152 | } 153 | 154 | int fastlz_compress_level(int level, const uint8_t* input, int length, uint8_t* output) 155 | { 156 | if(level == 1) 157 | return fastlz1_compress(input, length, output); 158 | if(level == 2) 159 | return fastlz2_compress(input, length, output); 160 | 161 | return 0; 162 | } 163 | 164 | #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ 165 | 166 | static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const uint8_t* input, int length, uint8_t* output) 167 | { 168 | const flzuint8* ip = (const flzuint8*) input; 169 | const flzuint8* ip_bound = ip + length - 2; 170 | const flzuint8* ip_limit = ip + length - 12; 171 | flzuint8* op = (flzuint8*) output; 172 | 173 | const flzuint8* htab[HASH_SIZE]; 174 | const flzuint8** hslot; 175 | flzuint32 hval; 176 | 177 | flzuint32 copy; 178 | 179 | /* sanity check */ 180 | if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) 181 | { 182 | if(length) 183 | { 184 | /* create literal copy only */ 185 | *op++ = length-1; 186 | ip_bound++; 187 | while(ip <= ip_bound) 188 | *op++ = *ip++; 189 | return length+1; 190 | } 191 | else 192 | return 0; 193 | } 194 | 195 | /* initializes hash table */ 196 | for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) 197 | *hslot = ip; 198 | 199 | /* we start with literal copy */ 200 | copy = 2; 201 | *op++ = MAX_COPY-1; 202 | *op++ = *ip++; 203 | *op++ = *ip++; 204 | 205 | /* main loop */ 206 | while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) 207 | { 208 | const flzuint8* ref; 209 | flzuint32 distance; 210 | 211 | /* minimum match length */ 212 | flzuint32 len = 3; 213 | 214 | /* comparison starting-point */ 215 | const flzuint8* anchor = ip; 216 | 217 | /* check for a run */ 218 | #if FASTLZ_LEVEL==2 219 | if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) 220 | { 221 | distance = 1; 222 | ip += 3; 223 | ref = anchor - 1 + 3; 224 | goto match; 225 | } 226 | #endif 227 | 228 | /* find potential match */ 229 | HASH_FUNCTION(hval,ip); 230 | hslot = htab + hval; 231 | ref = htab[hval]; 232 | 233 | /* calculate distance to the match */ 234 | distance = anchor - ref; 235 | 236 | /* update hash table */ 237 | *hslot = anchor; 238 | 239 | /* is this a match? check the first 3 bytes */ 240 | if(distance==0 || 241 | #if FASTLZ_LEVEL==1 242 | (distance >= MAX_DISTANCE) || 243 | #else 244 | (distance >= MAX_FARDISTANCE) || 245 | #endif 246 | *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) 247 | goto literal; 248 | 249 | #if FASTLZ_LEVEL==2 250 | /* far, needs at least 5-byte match */ 251 | if(distance >= MAX_DISTANCE) 252 | { 253 | if(*ip++ != *ref++ || *ip++!= *ref++) 254 | goto literal; 255 | len += 2; 256 | } 257 | 258 | match: 259 | #endif 260 | 261 | /* last matched byte */ 262 | ip = anchor + len; 263 | 264 | /* distance is biased */ 265 | distance--; 266 | 267 | if(!distance) 268 | { 269 | /* zero distance means a run */ 270 | flzuint8 x = ip[-1]; 271 | while(ip < ip_bound) 272 | if(*ref++ != x) break; else ip++; 273 | } 274 | else 275 | for(;;) 276 | { 277 | /* safe because the outer check against ip limit */ 278 | if(*ref++ != *ip++) break; 279 | if(*ref++ != *ip++) break; 280 | if(*ref++ != *ip++) break; 281 | if(*ref++ != *ip++) break; 282 | if(*ref++ != *ip++) break; 283 | if(*ref++ != *ip++) break; 284 | if(*ref++ != *ip++) break; 285 | if(*ref++ != *ip++) break; 286 | while(ip < ip_bound) 287 | if(*ref++ != *ip++) break; 288 | break; 289 | } 290 | 291 | /* if we have copied something, adjust the copy count */ 292 | if(copy) 293 | /* copy is biased, '0' means 1 byte copy */ 294 | *(op-copy-1) = copy-1; 295 | else 296 | /* back, to overwrite the copy count */ 297 | op--; 298 | 299 | /* reset literal counter */ 300 | copy = 0; 301 | 302 | /* length is biased, '1' means a match of 3 bytes */ 303 | ip -= 3; 304 | len = ip - anchor; 305 | 306 | /* encode the match */ 307 | #if FASTLZ_LEVEL==2 308 | if(distance < MAX_DISTANCE) 309 | { 310 | if(len < 7) 311 | { 312 | *op++ = (len << 5) + (distance >> 8); 313 | *op++ = (distance & 255); 314 | } 315 | else 316 | { 317 | *op++ = (7 << 5) + (distance >> 8); 318 | for(len-=7; len >= 255; len-= 255) 319 | *op++ = 255; 320 | *op++ = len; 321 | *op++ = (distance & 255); 322 | } 323 | } 324 | else 325 | { 326 | /* far away, but not yet in the another galaxy... */ 327 | if(len < 7) 328 | { 329 | distance -= MAX_DISTANCE; 330 | *op++ = (len << 5) + 31; 331 | *op++ = 255; 332 | *op++ = distance >> 8; 333 | *op++ = distance & 255; 334 | } 335 | else 336 | { 337 | distance -= MAX_DISTANCE; 338 | *op++ = (7 << 5) + 31; 339 | for(len-=7; len >= 255; len-= 255) 340 | *op++ = 255; 341 | *op++ = len; 342 | *op++ = 255; 343 | *op++ = distance >> 8; 344 | *op++ = distance & 255; 345 | } 346 | } 347 | #else 348 | 349 | if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) 350 | while(len > MAX_LEN-2) 351 | { 352 | *op++ = (7 << 5) + (distance >> 8); 353 | *op++ = MAX_LEN - 2 - 7 -2; 354 | *op++ = (distance & 255); 355 | len -= MAX_LEN-2; 356 | } 357 | 358 | if(len < 7) 359 | { 360 | *op++ = (len << 5) + (distance >> 8); 361 | *op++ = (distance & 255); 362 | } 363 | else 364 | { 365 | *op++ = (7 << 5) + (distance >> 8); 366 | *op++ = len - 7; 367 | *op++ = (distance & 255); 368 | } 369 | #endif 370 | 371 | /* update the hash at match boundary */ 372 | HASH_FUNCTION(hval,ip); 373 | htab[hval] = ip++; 374 | HASH_FUNCTION(hval,ip); 375 | htab[hval] = ip++; 376 | 377 | /* assuming literal copy */ 378 | *op++ = MAX_COPY-1; 379 | 380 | continue; 381 | 382 | literal: 383 | *op++ = *anchor++; 384 | ip = anchor; 385 | copy++; 386 | if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) 387 | { 388 | copy = 0; 389 | *op++ = MAX_COPY-1; 390 | } 391 | } 392 | 393 | /* left-over as literal copy */ 394 | ip_bound++; 395 | while(ip <= ip_bound) 396 | { 397 | *op++ = *ip++; 398 | copy++; 399 | if(copy == MAX_COPY) 400 | { 401 | copy = 0; 402 | *op++ = MAX_COPY-1; 403 | } 404 | } 405 | 406 | /* if we have copied something, adjust the copy length */ 407 | if(copy) 408 | *(op-copy-1) = copy-1; 409 | else 410 | op--; 411 | 412 | #if FASTLZ_LEVEL==2 413 | /* marker for fastlz2 */ 414 | *(flzuint8*)output |= (1 << 5); 415 | #endif 416 | 417 | return op - (flzuint8*)output; 418 | } 419 | 420 | static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const uint8_t* input, int length, uint8_t* output, int maxout) 421 | { 422 | const flzuint8* ip = (const flzuint8*) input; 423 | const flzuint8* ip_limit = ip + length; 424 | flzuint8* op = (flzuint8*) output; 425 | flzuint8* op_limit = op + maxout; 426 | flzuint32 ctrl = (*ip++) & 31; 427 | int loop = 1; 428 | 429 | do 430 | { 431 | const flzuint8* ref = op; 432 | flzuint32 len = ctrl >> 5; 433 | flzuint32 ofs = (ctrl & 31) << 8; 434 | 435 | if(ctrl >= 32) 436 | { 437 | #if FASTLZ_LEVEL==2 438 | flzuint8 code; 439 | #endif 440 | len--; 441 | ref -= ofs; 442 | if (len == 7-1) 443 | #if FASTLZ_LEVEL==1 444 | len += *ip++; 445 | ref -= *ip++; 446 | #else 447 | do 448 | { 449 | code = *ip++; 450 | len += code; 451 | } while (code==255); 452 | code = *ip++; 453 | ref -= code; 454 | 455 | /* match from 16-bit distance */ 456 | if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) 457 | if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) 458 | { 459 | ofs = (*ip++) << 8; 460 | ofs += *ip++; 461 | ref = op - ofs - MAX_DISTANCE; 462 | } 463 | #endif 464 | 465 | #ifdef FASTLZ_SAFE 466 | if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) 467 | return 0; 468 | 469 | if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) 470 | return 0; 471 | #endif 472 | 473 | if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) 474 | ctrl = *ip++; 475 | else 476 | loop = 0; 477 | 478 | if(ref == op) 479 | { 480 | /* optimize copy for a run */ 481 | flzuint8 b = ref[-1]; 482 | *op++ = b; 483 | *op++ = b; 484 | *op++ = b; 485 | for(; len; --len) 486 | *op++ = b; 487 | } 488 | else 489 | { 490 | #if !defined(FASTLZ_STRICT_ALIGN) 491 | const flzuint16* p; 492 | flzuint16* q; 493 | #endif 494 | /* copy from reference */ 495 | ref--; 496 | *op++ = *ref++; 497 | *op++ = *ref++; 498 | *op++ = *ref++; 499 | 500 | #if !defined(FASTLZ_STRICT_ALIGN) 501 | /* copy a byte, so that now it's word aligned */ 502 | if(len & 1) 503 | { 504 | *op++ = *ref++; 505 | len--; 506 | } 507 | 508 | /* copy 16-bit at once */ 509 | q = (flzuint16*) op; 510 | op += len; 511 | p = (const flzuint16*) ref; 512 | for(len>>=1; len > 4; len-=4) 513 | { 514 | *q++ = *p++; 515 | *q++ = *p++; 516 | *q++ = *p++; 517 | *q++ = *p++; 518 | } 519 | for(; len; --len) 520 | *q++ = *p++; 521 | #else 522 | for(; len; --len) 523 | *op++ = *ref++; 524 | #endif 525 | } 526 | } 527 | else 528 | { 529 | ctrl++; 530 | #ifdef FASTLZ_SAFE 531 | if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) 532 | return 0; 533 | if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) 534 | return 0; 535 | #endif 536 | 537 | *op++ = *ip++; 538 | for(--ctrl; ctrl; ctrl--) 539 | *op++ = *ip++; 540 | 541 | loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); 542 | if(loop) 543 | ctrl = *ip++; 544 | } 545 | } 546 | while(FASTLZ_EXPECT_CONDITIONAL(loop)); 547 | 548 | return op - (flzuint8*)output; 549 | } 550 | 551 | #endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ 552 | -------------------------------------------------------------------------------- /cython/wrap_arrays/fastlz.c: -------------------------------------------------------------------------------- 1 | /* 2 | FastLZ - lightning-fast lossless compression library 3 | 4 | Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) 5 | Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) 6 | Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | */ 26 | #include 27 | #if !defined(FASTLZ__COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) 28 | 29 | /* 30 | * Always check for bound when decompressing. 31 | * Generally it is best to leave it defined. 32 | */ 33 | #define FASTLZ_SAFE 34 | 35 | /* 36 | * Give hints to the compiler for branch prediction optimization. 37 | */ 38 | #if defined(__GNUC__) && (__GNUC__ > 2) 39 | #define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) 40 | #define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) 41 | #else 42 | #define FASTLZ_EXPECT_CONDITIONAL(c) (c) 43 | #define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) 44 | #endif 45 | 46 | /* 47 | * Use inlined functions for supported systems. 48 | */ 49 | #if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) 50 | #define FASTLZ_INLINE inline 51 | #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) 52 | #define FASTLZ_INLINE __inline 53 | #else 54 | #define FASTLZ_INLINE 55 | #endif 56 | 57 | /* 58 | * Prevent accessing more than 8-bit at once, except on x86 architectures. 59 | */ 60 | #if !defined(FASTLZ_STRICT_ALIGN) 61 | #define FASTLZ_STRICT_ALIGN 62 | #if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ 63 | #undef FASTLZ_STRICT_ALIGN 64 | #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ 65 | #undef FASTLZ_STRICT_ALIGN 66 | #elif defined(_M_IX86) /* Intel, MSVC */ 67 | #undef FASTLZ_STRICT_ALIGN 68 | #elif defined(__386) 69 | #undef FASTLZ_STRICT_ALIGN 70 | #elif defined(_X86_) /* MinGW */ 71 | #undef FASTLZ_STRICT_ALIGN 72 | #elif defined(__I86__) /* Digital Mars */ 73 | #undef FASTLZ_STRICT_ALIGN 74 | #endif 75 | #endif 76 | 77 | /* 78 | * FIXME: use preprocessor magic to set this on different platforms! 79 | */ 80 | typedef unsigned char flzuint8; 81 | typedef unsigned short flzuint16; 82 | typedef unsigned int flzuint32; 83 | 84 | /* prototypes */ 85 | int fastlz_compress(const uint8_t* input, int length, uint8_t* output); 86 | int fastlz_compress_level(int level, const uint8_t* input, int length, uint8_t* output); 87 | int fastlz_decompress(const uint8_t* input, int length, uint8_t* output, int maxout); 88 | 89 | #define MAX_COPY 32 90 | #define MAX_LEN 264 /* 256 + 8 */ 91 | #define MAX_DISTANCE 8192 92 | 93 | #if !defined(FASTLZ_STRICT_ALIGN) 94 | #define FASTLZ_READU16(p) *((const flzuint16*)(p)) 95 | #else 96 | #define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) 97 | #endif 98 | 99 | #define HASH_LOG 13 100 | #define HASH_SIZE (1<< HASH_LOG) 101 | #define HASH_MASK (HASH_SIZE-1) 102 | #define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } 103 | 104 | #undef FASTLZ_LEVEL 105 | #define FASTLZ_LEVEL 1 106 | 107 | #undef FASTLZ_COMPRESSOR 108 | #undef FASTLZ_DECOMPRESSOR 109 | #define FASTLZ_COMPRESSOR fastlz1_compress 110 | #define FASTLZ_DECOMPRESSOR fastlz1_decompress 111 | static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const uint8_t* input, int length, uint8_t* output); 112 | static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const uint8_t* input, int length, uint8_t* output, int maxout); 113 | #include "fastlz.c" 114 | 115 | #undef FASTLZ_LEVEL 116 | #define FASTLZ_LEVEL 2 117 | 118 | #undef MAX_DISTANCE 119 | #define MAX_DISTANCE 8191 120 | #define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) 121 | 122 | #undef FASTLZ_COMPRESSOR 123 | #undef FASTLZ_DECOMPRESSOR 124 | #define FASTLZ_COMPRESSOR fastlz2_compress 125 | #define FASTLZ_DECOMPRESSOR fastlz2_decompress 126 | static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const uint8_t* input, int length, uint8_t* output); 127 | static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const uint8_t* input, int length, uint8_t* output, int maxout); 128 | #include "fastlz.c" 129 | 130 | int fastlz_compress(const uint8_t* input, int length, uint8_t* output) 131 | { 132 | /* for short block, choose fastlz1 */ 133 | if(length < 65536) 134 | return fastlz1_compress(input, length, output); 135 | 136 | /* else... */ 137 | return fastlz2_compress(input, length, output); 138 | } 139 | 140 | int fastlz_decompress(const uint8_t* input, int length, uint8_t* output, int maxout) 141 | { 142 | /* magic identifier for compression level */ 143 | int level = ((*(const flzuint8*)input) >> 5) + 1; 144 | 145 | if(level == 1) 146 | return fastlz1_decompress(input, length, output, maxout); 147 | if(level == 2) 148 | return fastlz2_decompress(input, length, output, maxout); 149 | 150 | /* unknown level, trigger error */ 151 | return 0; 152 | } 153 | 154 | int fastlz_compress_level(int level, const uint8_t* input, int length, uint8_t* output) 155 | { 156 | if(level == 1) 157 | return fastlz1_compress(input, length, output); 158 | if(level == 2) 159 | return fastlz2_compress(input, length, output); 160 | 161 | return 0; 162 | } 163 | 164 | #else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ 165 | 166 | static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const uint8_t* input, int length, uint8_t* output) 167 | { 168 | const flzuint8* ip = (const flzuint8*) input; 169 | const flzuint8* ip_bound = ip + length - 2; 170 | const flzuint8* ip_limit = ip + length - 12; 171 | flzuint8* op = (flzuint8*) output; 172 | 173 | const flzuint8* htab[HASH_SIZE]; 174 | const flzuint8** hslot; 175 | flzuint32 hval; 176 | 177 | flzuint32 copy; 178 | 179 | /* sanity check */ 180 | if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) 181 | { 182 | if(length) 183 | { 184 | /* create literal copy only */ 185 | *op++ = length-1; 186 | ip_bound++; 187 | while(ip <= ip_bound) 188 | *op++ = *ip++; 189 | return length+1; 190 | } 191 | else 192 | return 0; 193 | } 194 | 195 | /* initializes hash table */ 196 | for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) 197 | *hslot = ip; 198 | 199 | /* we start with literal copy */ 200 | copy = 2; 201 | *op++ = MAX_COPY-1; 202 | *op++ = *ip++; 203 | *op++ = *ip++; 204 | 205 | /* main loop */ 206 | while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) 207 | { 208 | const flzuint8* ref; 209 | flzuint32 distance; 210 | 211 | /* minimum match length */ 212 | flzuint32 len = 3; 213 | 214 | /* comparison starting-point */ 215 | const flzuint8* anchor = ip; 216 | 217 | /* check for a run */ 218 | #if FASTLZ_LEVEL==2 219 | if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) 220 | { 221 | distance = 1; 222 | ip += 3; 223 | ref = anchor - 1 + 3; 224 | goto match; 225 | } 226 | #endif 227 | 228 | /* find potential match */ 229 | HASH_FUNCTION(hval,ip); 230 | hslot = htab + hval; 231 | ref = htab[hval]; 232 | 233 | /* calculate distance to the match */ 234 | distance = anchor - ref; 235 | 236 | /* update hash table */ 237 | *hslot = anchor; 238 | 239 | /* is this a match? check the first 3 bytes */ 240 | if(distance==0 || 241 | #if FASTLZ_LEVEL==1 242 | (distance >= MAX_DISTANCE) || 243 | #else 244 | (distance >= MAX_FARDISTANCE) || 245 | #endif 246 | *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) 247 | goto literal; 248 | 249 | #if FASTLZ_LEVEL==2 250 | /* far, needs at least 5-byte match */ 251 | if(distance >= MAX_DISTANCE) 252 | { 253 | if(*ip++ != *ref++ || *ip++!= *ref++) 254 | goto literal; 255 | len += 2; 256 | } 257 | 258 | match: 259 | #endif 260 | 261 | /* last matched byte */ 262 | ip = anchor + len; 263 | 264 | /* distance is biased */ 265 | distance--; 266 | 267 | if(!distance) 268 | { 269 | /* zero distance means a run */ 270 | flzuint8 x = ip[-1]; 271 | while(ip < ip_bound) 272 | if(*ref++ != x) break; else ip++; 273 | } 274 | else 275 | for(;;) 276 | { 277 | /* safe because the outer check against ip limit */ 278 | if(*ref++ != *ip++) break; 279 | if(*ref++ != *ip++) break; 280 | if(*ref++ != *ip++) break; 281 | if(*ref++ != *ip++) break; 282 | if(*ref++ != *ip++) break; 283 | if(*ref++ != *ip++) break; 284 | if(*ref++ != *ip++) break; 285 | if(*ref++ != *ip++) break; 286 | while(ip < ip_bound) 287 | if(*ref++ != *ip++) break; 288 | break; 289 | } 290 | 291 | /* if we have copied something, adjust the copy count */ 292 | if(copy) 293 | /* copy is biased, '0' means 1 byte copy */ 294 | *(op-copy-1) = copy-1; 295 | else 296 | /* back, to overwrite the copy count */ 297 | op--; 298 | 299 | /* reset literal counter */ 300 | copy = 0; 301 | 302 | /* length is biased, '1' means a match of 3 bytes */ 303 | ip -= 3; 304 | len = ip - anchor; 305 | 306 | /* encode the match */ 307 | #if FASTLZ_LEVEL==2 308 | if(distance < MAX_DISTANCE) 309 | { 310 | if(len < 7) 311 | { 312 | *op++ = (len << 5) + (distance >> 8); 313 | *op++ = (distance & 255); 314 | } 315 | else 316 | { 317 | *op++ = (7 << 5) + (distance >> 8); 318 | for(len-=7; len >= 255; len-= 255) 319 | *op++ = 255; 320 | *op++ = len; 321 | *op++ = (distance & 255); 322 | } 323 | } 324 | else 325 | { 326 | /* far away, but not yet in the another galaxy... */ 327 | if(len < 7) 328 | { 329 | distance -= MAX_DISTANCE; 330 | *op++ = (len << 5) + 31; 331 | *op++ = 255; 332 | *op++ = distance >> 8; 333 | *op++ = distance & 255; 334 | } 335 | else 336 | { 337 | distance -= MAX_DISTANCE; 338 | *op++ = (7 << 5) + 31; 339 | for(len-=7; len >= 255; len-= 255) 340 | *op++ = 255; 341 | *op++ = len; 342 | *op++ = 255; 343 | *op++ = distance >> 8; 344 | *op++ = distance & 255; 345 | } 346 | } 347 | #else 348 | 349 | if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) 350 | while(len > MAX_LEN-2) 351 | { 352 | *op++ = (7 << 5) + (distance >> 8); 353 | *op++ = MAX_LEN - 2 - 7 -2; 354 | *op++ = (distance & 255); 355 | len -= MAX_LEN-2; 356 | } 357 | 358 | if(len < 7) 359 | { 360 | *op++ = (len << 5) + (distance >> 8); 361 | *op++ = (distance & 255); 362 | } 363 | else 364 | { 365 | *op++ = (7 << 5) + (distance >> 8); 366 | *op++ = len - 7; 367 | *op++ = (distance & 255); 368 | } 369 | #endif 370 | 371 | /* update the hash at match boundary */ 372 | HASH_FUNCTION(hval,ip); 373 | htab[hval] = ip++; 374 | HASH_FUNCTION(hval,ip); 375 | htab[hval] = ip++; 376 | 377 | /* assuming literal copy */ 378 | *op++ = MAX_COPY-1; 379 | 380 | continue; 381 | 382 | literal: 383 | *op++ = *anchor++; 384 | ip = anchor; 385 | copy++; 386 | if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) 387 | { 388 | copy = 0; 389 | *op++ = MAX_COPY-1; 390 | } 391 | } 392 | 393 | /* left-over as literal copy */ 394 | ip_bound++; 395 | while(ip <= ip_bound) 396 | { 397 | *op++ = *ip++; 398 | copy++; 399 | if(copy == MAX_COPY) 400 | { 401 | copy = 0; 402 | *op++ = MAX_COPY-1; 403 | } 404 | } 405 | 406 | /* if we have copied something, adjust the copy length */ 407 | if(copy) 408 | *(op-copy-1) = copy-1; 409 | else 410 | op--; 411 | 412 | #if FASTLZ_LEVEL==2 413 | /* marker for fastlz2 */ 414 | *(flzuint8*)output |= (1 << 5); 415 | #endif 416 | 417 | return op - (flzuint8*)output; 418 | } 419 | 420 | static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const uint8_t* input, int length, uint8_t* output, int maxout) 421 | { 422 | const flzuint8* ip = (const flzuint8*) input; 423 | const flzuint8* ip_limit = ip + length; 424 | flzuint8* op = (flzuint8*) output; 425 | flzuint8* op_limit = op + maxout; 426 | flzuint32 ctrl = (*ip++) & 31; 427 | int loop = 1; 428 | 429 | do 430 | { 431 | const flzuint8* ref = op; 432 | flzuint32 len = ctrl >> 5; 433 | flzuint32 ofs = (ctrl & 31) << 8; 434 | 435 | if(ctrl >= 32) 436 | { 437 | #if FASTLZ_LEVEL==2 438 | flzuint8 code; 439 | #endif 440 | len--; 441 | ref -= ofs; 442 | if (len == 7-1) 443 | #if FASTLZ_LEVEL==1 444 | len += *ip++; 445 | ref -= *ip++; 446 | #else 447 | do 448 | { 449 | code = *ip++; 450 | len += code; 451 | } while (code==255); 452 | code = *ip++; 453 | ref -= code; 454 | 455 | /* match from 16-bit distance */ 456 | if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) 457 | if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) 458 | { 459 | ofs = (*ip++) << 8; 460 | ofs += *ip++; 461 | ref = op - ofs - MAX_DISTANCE; 462 | } 463 | #endif 464 | 465 | #ifdef FASTLZ_SAFE 466 | if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) 467 | return 0; 468 | 469 | if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) 470 | return 0; 471 | #endif 472 | 473 | if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) 474 | ctrl = *ip++; 475 | else 476 | loop = 0; 477 | 478 | if(ref == op) 479 | { 480 | /* optimize copy for a run */ 481 | flzuint8 b = ref[-1]; 482 | *op++ = b; 483 | *op++ = b; 484 | *op++ = b; 485 | for(; len; --len) 486 | *op++ = b; 487 | } 488 | else 489 | { 490 | #if !defined(FASTLZ_STRICT_ALIGN) 491 | const flzuint16* p; 492 | flzuint16* q; 493 | #endif 494 | /* copy from reference */ 495 | ref--; 496 | *op++ = *ref++; 497 | *op++ = *ref++; 498 | *op++ = *ref++; 499 | 500 | #if !defined(FASTLZ_STRICT_ALIGN) 501 | /* copy a byte, so that now it's word aligned */ 502 | if(len & 1) 503 | { 504 | *op++ = *ref++; 505 | len--; 506 | } 507 | 508 | /* copy 16-bit at once */ 509 | q = (flzuint16*) op; 510 | op += len; 511 | p = (const flzuint16*) ref; 512 | for(len>>=1; len > 4; len-=4) 513 | { 514 | *q++ = *p++; 515 | *q++ = *p++; 516 | *q++ = *p++; 517 | *q++ = *p++; 518 | } 519 | for(; len; --len) 520 | *q++ = *p++; 521 | #else 522 | for(; len; --len) 523 | *op++ = *ref++; 524 | #endif 525 | } 526 | } 527 | else 528 | { 529 | ctrl++; 530 | #ifdef FASTLZ_SAFE 531 | if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) 532 | return 0; 533 | if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) 534 | return 0; 535 | #endif 536 | 537 | *op++ = *ip++; 538 | for(--ctrl; ctrl; ctrl--) 539 | *op++ = *ip++; 540 | 541 | loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); 542 | if(loop) 543 | ctrl = *ip++; 544 | } 545 | } 546 | while(FASTLZ_EXPECT_CONDITIONAL(loop)); 547 | 548 | return op - (flzuint8*)output; 549 | } 550 | 551 | #endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ 552 | --------------------------------------------------------------------------------