Skip to content
Snippets Groups Projects
utils.h 11.53 KiB
#include "wrap_cl.h"
#include "function.h"

#include <string>
#include <sstream>
#include <string.h>
#include <memory>

#ifndef __PYOPENCL_UTILS_H
#define __PYOPENCL_UTILS_H

#if (defined(__GNUC__) && (__GNUC__ > 2))
#  define PYOPENCL_EXPECT(exp, var) __builtin_expect(exp, var)
#else
#  define PYOPENCL_EXPECT(exp, var) (exp)
#endif

#define PYOPENCL_LIKELY(x) PYOPENCL_EXPECT(bool(x), true)
#define PYOPENCL_UNLIKELY(x) PYOPENCL_EXPECT(bool(x), false)

template<class T>
PYOPENCL_USE_RESULT static PYOPENCL_INLINE std::string
tostring(const T& v)
{
    std::ostringstream ostr;
    ostr << v;
    return ostr.str();
}

namespace pyopencl {

enum class ArgType {
    None,
    SizeOf,
    Length,
};

template<typename T>
static PYOPENCL_INLINE void
_print_buf_content(std::ostream &stm, const T *p, size_t len)
{
    if (len > 1) {
        stm << "[";
    }
    for (size_t i = 0;i < len;i++) {
        stm << p[i];
        if (i != len - 1) {
            stm << ", ";
        }
    }
    if (len > 1) {
        stm << "]";
    }
}

template<>
PYOPENCL_INLINE void
_print_buf_content<char>(std::ostream &stm, const char *p, size_t len)
{
    stm << '"';
    stm.write(p, len);
    stm << '"';
}

template<typename T>
void
print_buf(std::ostream &stm, const T *p, size_t len,
          ArgType arg_type, bool content, bool out)
{
    const size_t ele_size = sizeof(T);
    if (out) {
        stm << "*(" << (const void*)p << "): ";
        _print_buf_content(stm, p, len);
    } else {
        if (content) {
            _print_buf_content(stm, p, len);
            stm << " <";
        }
        switch (arg_type) {
        case ArgType::SizeOf:
            stm << ele_size * len << ", ";
            break;
        case ArgType::Length:
            stm << len << ", ";
            break;
        default:
            break;
        }
        stm << (const void*)p;
        if (content) {
            stm << ">";
        }
    }
}

template<typename T>
void
print_arg(std::ostream &stm, const T &v, bool out)
{
    if (!out) {
        stm << (const void*)&v;
    } else {
        stm << "*(" << (const void*)&v << "): " << v;
    }
}
extern template void print_buf<char>(std::ostream&, const char*, size_t,
                                     ArgType, bool, bool);
extern template void print_buf<cl_int>(std::ostream&, const cl_int*, size_t,
                                       ArgType, bool, bool);
extern template void print_buf<cl_uint>(std::ostream&, const cl_uint*, size_t,
                                        ArgType, bool, bool);
extern template void print_buf<cl_long>(std::ostream&, const cl_long*, size_t,
                                        ArgType, bool, bool);
extern template void print_buf<cl_ulong>(std::ostream&, const cl_ulong*, size_t,
                                         ArgType, bool, bool);
extern template void print_buf<cl_image_format>(std::ostream&,
                                                const cl_image_format*, size_t,
                                                ArgType, bool, bool);

// TODO
template<typename T, class = void>
struct CLGenericArgPrinter {
    static PYOPENCL_INLINE void
    print(std::ostream &stm, T &arg, bool)
    {
        stm << arg;
    }
};

template<>
struct CLGenericArgPrinter<std::nullptr_t, void> {
    static PYOPENCL_INLINE void
    print(std::ostream &stm, std::nullptr_t&, bool)
    {
        stm << (void*)nullptr;
    }
};

template<typename T, class = void>
struct CLGenericArgOut : std::false_type {};

template<typename T, class = void>
class CLArg {
private:
    T &m_arg;
public:
    constexpr static bool is_out = CLGenericArgOut<T>::value;
    CLArg(T &arg) noexcept
        : m_arg(arg)
    {}
    CLArg(CLArg &&other) noexcept
        : m_arg(other.m_arg)
    {}
    PYOPENCL_INLINE T&
    convert() noexcept
    {
        return m_arg;
    }
    PYOPENCL_INLINE void
    print(std::ostream &stm, bool out=false)
    {
        CLGenericArgPrinter<T>::print(stm, m_arg, out);
    }
};

template<typename T, ArgType AT=ArgType::None>
class ArgBuffer {
private:
    T *m_buf;
    size_t m_len;
protected:
    PYOPENCL_INLINE void
    set(T *buf) noexcept
    {
        m_buf = buf;
    }
public:
    typedef T type;
    constexpr static ArgType arg_type = AT;
    ArgBuffer(T *buf, size_t l) noexcept
        : m_buf(buf), m_len(l)
    {}
    ArgBuffer(ArgBuffer<T, AT> &&other) noexcept
        : ArgBuffer(other.m_buf, other.m_len)
    {}
    PYOPENCL_INLINE T*
    get() const noexcept
    {
        return m_buf;
    }
    PYOPENCL_INLINE size_t
    len() const noexcept
    {
        return m_len;
    }
};

template<ArgType AT, typename T, class = void>
struct _ToArgBuffer {
    static PYOPENCL_INLINE ArgBuffer<rm_ref_t<T>, AT>
    convert(T &buf)
    {
        return ArgBuffer<rm_ref_t<T>, AT>(&buf, 1);
    }
};

template<ArgType AT=ArgType::None, typename T>
static PYOPENCL_INLINE auto
buf_arg(T &&buf)
    -> decltype(_ToArgBuffer<AT, T>::convert(std::forward<T>(buf)))
{
    return _ToArgBuffer<AT, T>::convert(std::forward<T>(buf));
}

template<ArgType AT=ArgType::None, typename T>
static PYOPENCL_INLINE ArgBuffer<T, AT>
buf_arg(T *buf, size_t l)
{
    return ArgBuffer<T, AT>(buf, l);
}

template<typename T>
static PYOPENCL_INLINE ArgBuffer<T, ArgType::SizeOf>
size_arg(T &buf)
{
    return ArgBuffer<T, ArgType::SizeOf>(&buf, 1);
}

template<typename Buff, class = void>
struct _ArgBufferConverter;

template<typename Buff>
struct _ArgBufferConverter<Buff,
                           enable_if_t<Buff::arg_type == ArgType::None> > {
    static PYOPENCL_INLINE typename Buff::type*
    convert(Buff &buff)
    {
        return buff.get();
    }
};

template<typename Buff>
struct _ArgBufferConverter<Buff,
                           enable_if_t<Buff::arg_type == ArgType::SizeOf> > {
    static PYOPENCL_INLINE auto
    convert(Buff &buff)
        -> decltype(std::make_tuple(sizeof(typename Buff::type) * buff.len(),
                                    buff.get()))
    {
        return std::make_tuple(sizeof(typename Buff::type) * buff.len(),
                               buff.get());
    }
};

template<typename Buff>
struct _ArgBufferConverter<Buff,
                           enable_if_t<Buff::arg_type == ArgType::Length> > {
    static PYOPENCL_INLINE auto
    convert(Buff &buff)
        -> decltype(std::make_tuple(buff.len(), buff.get()))
    {
        return std::make_tuple(buff.len(), buff.get());
    }
};

template<typename Buff>
class CLArg<Buff, enable_if_t<std::is_base_of<ArgBuffer<typename Buff::type,
                                                        Buff::arg_type>,
                                              Buff>::value> > {
private:
    Buff &m_buff;
public:
    constexpr static bool is_out = !std::is_const<typename Buff::type>::value;
    CLArg(Buff &buff) noexcept
        : m_buff(buff)
    {}
    CLArg(CLArg<Buff> &&other) noexcept
        : m_buff(other.m_buff)
    {}
    PYOPENCL_INLINE auto
    convert() const noexcept
        -> decltype(_ArgBufferConverter<Buff>::convert(m_buff))
    {
        return _ArgBufferConverter<Buff>::convert(m_buff);
    }
    PYOPENCL_INLINE void
    print(std::ostream &stm, bool out=false)
    {
        print_buf(stm, m_buff.get(), m_buff.len(),
                  Buff::arg_type, out || !is_out, out);
    }
};

template<typename T, size_t n, ArgType AT=ArgType::None>
class ConstBuffer : public ArgBuffer<const T, AT> {
private:
    T m_intern_buf[n];
    ConstBuffer(ConstBuffer<T, n, AT>&&) = delete;
    ConstBuffer() = delete;
public:
    ConstBuffer(const T *buf, size_t l)
        : ArgBuffer<const T, AT>(buf, n)
    {
        if (l < n) {
            memcpy(m_intern_buf, buf, sizeof(T) * std::min(l, n));
            this->set(m_intern_buf);
        }
    }
};

struct OutArg {
};

template<typename T>
class CLArg<T, enable_if_t<std::is_base_of<OutArg, T>::value> > {
private:
    bool m_converted;
    bool m_need_cleanup;
    T &m_arg;
public:
    constexpr static bool is_out = true;
    CLArg(T &arg)
        : m_converted(false), m_need_cleanup(false), m_arg(arg)
    {
    }
    CLArg(CLArg<T> &&other) noexcept
        : m_converted(other.m_converted), m_need_cleanup(other.m_need_cleanup),
        m_arg(other.m_arg)
    {
        other.m_need_cleanup = false;
    }
    PYOPENCL_INLINE auto
    convert()
        -> decltype(m_arg.get())
    {
        return m_arg.get();
    }
    PYOPENCL_INLINE void
    finish(bool converted) noexcept
    {
        m_need_cleanup = !converted;
    }
    PYOPENCL_INLINE void
    post()
    {
        m_arg.convert();
        m_converted = true;
    }
    ~CLArg()
    {
        if (m_need_cleanup) {
            m_arg.cleanup(m_converted);
        }
    }
    PYOPENCL_INLINE void
    print(std::ostream &stm, bool out=false)
    {
        m_arg.print(stm, out);
    }
};

template<typename T>
struct _D {
    void operator()(T *p) {
        free((void*)p);
    }
};

template<typename T>
class pyopencl_buf : public std::unique_ptr<T, _D<T> > {
    size_t m_len;
public:
    PYOPENCL_INLINE
    pyopencl_buf(size_t len=1)
        : std::unique_ptr<T, _D<T> >((T*)(len ? malloc(sizeof(T) * (len + 1)) :
                                          nullptr)), m_len(len)
    {
        if (len) {
            memset((void*)this->get(), 0, (len + 1) * sizeof(T));
        }
    }
    PYOPENCL_INLINE size_t
    len() const
    {
        return m_len;
    }
    PYOPENCL_INLINE T&
    operator[](int i)
    {
        return this->get()[i];
    }
    PYOPENCL_INLINE const T&
    operator[](int i) const
    {
        return this->get()[i];
    }
    PYOPENCL_INLINE void
    resize(size_t len)
    {
        if (len == m_len)
            return;
        m_len = len;
        this->reset((T*)realloc((void*)this->release(),
                                (len + 1) * sizeof(T)));
    }
};

template<typename T>
using pyopencl_buf_ele_t = typename rm_ref_t<T>::element_type;

template<typename T, class = void>
struct is_pyopencl_buf : std::false_type {};

template<typename T>
struct is_pyopencl_buf<
    T, enable_if_t<std::is_base_of<pyopencl_buf<pyopencl_buf_ele_t<T> >,
                                   rm_ref_t<T> >::value> > : std::true_type {};

template<ArgType AT, typename T>
struct _ToArgBuffer<AT, T, enable_if_t<is_pyopencl_buf<T>::value &&
                                       std::is_const<rm_ref_t<T> >::value> > {
    static PYOPENCL_INLINE ArgBuffer<const pyopencl_buf_ele_t<T>, AT>
    convert(T &&buf)
    {
        return ArgBuffer<const pyopencl_buf_ele_t<T>, AT>(buf.get(), buf.len());
    }
};

template<ArgType AT, typename T>
struct _ToArgBuffer<AT, T, enable_if_t<is_pyopencl_buf<T>::value &&
                                       !std::is_const<rm_ref_t<T> >::value> > {
    static PYOPENCL_INLINE ArgBuffer<pyopencl_buf_ele_t<T>, AT>
    convert(T &&buf)
    {
        return ArgBuffer<pyopencl_buf_ele_t<T>, AT>(buf.get(), buf.len());
    }
};

template<typename Buff>
using __pyopencl_buf_arg_type =
    rm_ref_t<decltype(buf_arg<ArgType::Length>(std::declval<Buff&>()))>;

template<typename Buff>
class CLArg<Buff, enable_if_t<is_pyopencl_buf<Buff>::value> >
    : public CLArg<__pyopencl_buf_arg_type<Buff> > {
    typedef __pyopencl_buf_arg_type<Buff> BufType;
    BufType m_buff;
public:
    PYOPENCL_INLINE
    CLArg(Buff &buff) noexcept
        : CLArg<BufType>(m_buff), m_buff(buf_arg<ArgType::Length>(buff))
    {}
    PYOPENCL_INLINE
    CLArg(CLArg<Buff> &&other) noexcept
        : CLArg<BufType>(m_buff), m_buff(std::move(other.m_buff))
    {}
};

template<typename T>
static PYOPENCL_INLINE cl_bool
cast_bool(const T &v)
{
    return v ? CL_TRUE : CL_FALSE;
}

// FIXME
PYOPENCL_USE_RESULT static PYOPENCL_INLINE char*
_copy_str(const std::string& str)
{
    return strdup(str.c_str());
}

}

#endif