Re: LP_c_ubyte buffer and length to python string?
Mads Kiilerich <[email protected]> Sun, 24 Jul 2011 22:25:06 +0200
| Newsgroups | gmane.comp.python.ctypes |
|---|---|
| Message-ID | <[email protected]> |
Dan Stromberg wrote, On 07/24/2011 09:09 PM:
> Is it the case that ctypes variables are garbage collected, as long as
> the functions you're calling don't do their own malloc'ing or other
> form of memory allocation?
ctypes objects are Python objects and have a lifetime like other Python
objects (ie reference counting and occasional garbage collection in
c-python), so you have to make sure you keep a reference to the objects
as long as you reference their memory.
>
> You might want to post a minimal self-contained code snippet that
> shows what function you are calling and how you are doing it.
>
>
> I'm calling 3 functions:
> 1) One gets an upper bound on how big the compressed size of a block
> can be
> 2) The next compresses a block of data
> 3) The last uncompresses a block of data
>
> I'm not sure how self-contained this is, but here's the class.
This class wrapping is definitely not minimal ...
> @classmethod
> def declare_c_function(cls, library, name, argtypes=None,
> restype=None):
> '''Extract functions from liblzma'''
> try:
> func = getattr(cls.LZMA_LIB, '_%s' % name)
> cls.funcs[name] = func
> except AttributeError:
> func = getattr(cls.LZMA_LIB, name)
> cls.funcs[name] = func
> if argtypes is not None:
> func.argtypes = argtypes
> if restype is not None:
> func.restype = restype
>
> @classmethod
> def class_init(cls):
> cls.LZMA_LIBPATH = _find_lib('lzma')
> cls.C_LIBPATH = _find_lib('c')
>
> # *ix way - windows is different
> cls.LZMA_LIB = ctypes.CDLL(cls.LZMA_LIBPATH)
> cls.C_LIB = ctypes.CDLL(cls.LZMA_LIBPATH)
>
> cls.declare_c_function(cls.LZMA_LIB,
> 'lzma_stream_buffer_bound', (ctypes.c_size_t, ), ctypes.c_size_t)
Why not just use the more simple pattern from
http://docs.python.org/library/ctypes#return-types ? Not that I care,
but I would recommend keeping it simple. ;-)
> # lzma_easy_buffer_encode notes:
> # Argument 1, uint32_t preset:
> # Just a uint32 - simple
> # Argument 2, lzma_check check:
> # An enum to the C programmer - that should usually be an
> unsigned integer in the C runtime.
> # Argument 3, lzma_allocator *lzma_allocator:
> # This is really a pointer to a struct, but happily, we
> only need to pass NULL to it, so we can just treat it as a void *
> # Argument 4: uint8_t *in:
> # Argument 5: size_t in_size:
> # Argument 6: uint8_t *out
> # Argument 7: size_t *out_pos
> # Argument 8: size_t out_size
> #
> # The return type is also an enum, so probably an unsigned int
> c_size_t_p = ctypes.POINTER(ctypes.c_size_t)
> cls.declare_c_function(
> cls.LZMA_LIB,
> 'lzma_easy_buffer_encode',
> (
> ctypes.c_uint32, # preset
> ctypes.c_uint, # check
> ctypes.c_void_p, # lzma_allocator
> ctypes.POINTER(ctypes.c_uint8), # uint8_t *in
> ctypes.c_size_t, # size_t in_size
> ctypes.POINTER(ctypes.c_uint8), # uint8_t *out
> ctypes.c_void_p, # size_t *out_pos
> # ctypes made size_t * think it was a long, so we use void_p
> ctypes.c_size_t, # size_t out_size
> ),
> ctypes.c_uint,
> )
> @classmethod
> def compress(cls, input_data):
> '''Compress data into xz format using ctypes to access
> liblzma.so'''
> # maximum_size = lzma_stream_buffer_bound(input_buffer_size);
> length_input_data = len(input_data)
> maximum_size =
> cls.funcs['lzma_stream_buffer_bound'](length_input_data)
>
> # This is an efficient way of creating a readonly ctypes string
> ctypes_input_data_char_p = ctypes.c_char_p(input_data)
> ctypes_input_data = ctypes.cast(ctypes_input_data_char_p,
> ctypes.POINTER(ctypes.c_ubyte))
Note that most of this casting and conversion is done automatically when
argtypes is specified correctly.
Specify this function parameter type as c_char_p and then you can give
it input_data directly.
> # This is a less-fast but mutable way of creating a ctypes string
> ctypes_compressed_buffer_char_p =
> ctypes.create_string_buffer(maximum_size)
> ctypes_compressed_buffer =
> ctypes.cast(ctypes_compressed_buffer_char_p,
> ctypes.POINTER(ctypes.c_ubyte))
Specify this function parameter type as c_char_p and then you can give
it the string buffer directly.
> ctypes_compressed_size = ctypes.c_size_t(0)
> #ctypes_compressed_size_pointer = ctypes.POINTER(ctypes.c_size_t)
> ctypes_compressed_size_pointer =
> ctypes.cast(ctypes.addressof(ctypes_compressed_size), ctypes.c_void_p)
Specify this function parameter type as POINTER(c_size_t) and then you
can give it byref(ctypes_compressed_size).
>
> # lzma_easy_buffer_encode
> ret_xz = cls.funcs['lzma_easy_buffer_encode'](
> cls.LZMA_PRESET_DEFAULT,
> cls.LZMA_CHECK_CRC32,
> None,
> ctypes_input_data,
> length_input_data,
> ctypes_compressed_buffer,
> ctypes_compressed_size_pointer,
> maximum_size,
> )
> if ret_xz != cls.LZMA_OK:
> raise OSError(cls.get_xz_error(ret_xz))
>
> resultant_length = int(ctypes_compressed_size.value)
You have already specified the return type as int so there is no reason
to specify it again here.
> result = ctypes_compressed_buffer_char_p.raw[:resultant_length]
>
> return result
/Mads
------------------------------------------------------------------------------
Magic Quadrant for Content-Aware Data Loss Prevention
Research study explores the data loss prevention market. Includes in-depth
analysis on the changes within the DLP market, and the criteria used to
evaluate the strengths and weaknesses of these DLP solutions.
http://www.accelacomm.com/jaw/sfnl/114/51385063/