From 0f3c3999e35153c0790d75512ba4f666e591a66c Mon Sep 17 00:00:00 2001 From: Kai Huuhko Date: Tue, 26 Mar 2013 18:53:13 +0000 Subject: [PATCH] Make the conv functions more robust. Now we may leak the strings from _(c)fruni which should be looked up case by case. The positive side is that we no longer point to (possibly) invalid memory. Strings used with touni funcs are suspected to leak as well. --- efl/elementary/window.pyx | 36 ++++++------ efl/eo/efl.eo.pyx | 113 ++++++++++++++++++++++++++++---------- include/efl.eo.pxd | 10 +--- 3 files changed, 102 insertions(+), 57 deletions(-) diff --git a/efl/elementary/window.pyx b/efl/elementary/window.pyx index b2ba719..64b9dbd 100644 --- a/efl/elementary/window.pyx +++ b/efl/elementary/window.pyx @@ -221,7 +221,8 @@ include "widget_header.pxi" -from libc.stdlib cimport free +from libc.stdlib cimport malloc, free +from libc.string cimport memcpy from object cimport Object @@ -756,31 +757,26 @@ cdef class Window(Object): """ def __set__(self, list profiles): - cdef unsigned int count = len(profiles) - elm_win_available_profiles_set(self.obj, convert_python_list_strings_to_array_of_strings(profiles), count) + self.available_profiles_set(profiles) def __get__(self): - cdef: - char **profiles - unsigned int count + return self.available_profiles_get() - ret = elm_win_available_profiles_get(self.obj, &profiles, &count) - if ret is 0: - raise RuntimeError("No available profiles") - - return convert_array_of_strings_to_python_list(profiles, count) - - def available_profiles_set(self, list profiles): + cpdef available_profiles_set(self, list profiles): cdef: - unsigned int count = len(profiles) - const_char **lst = convert_python_list_strings_to_array_of_strings(profiles) + const_char **array + unsigned int arr_len = len(profiles) + unsigned int i - elm_win_available_profiles_set(self.obj, lst, count) - for i in range(count): - free(lst[i]) - free(lst) + try: + array = convert_python_list_strings_to_array_of_strings(profiles) + elm_win_available_profiles_set(self.obj, array, arr_len) + finally: + for i in range(arr_len): + free(array[i]) + free(array) - def available_profiles_get(self): + cpdef available_profiles_get(self): cdef: char **profiles unsigned int count diff --git a/efl/eo/efl.eo.pyx b/efl/eo/efl.eo.pyx index fbc5814..1821ad3 100644 --- a/efl/eo/efl.eo.pyx +++ b/efl/eo/efl.eo.pyx @@ -17,6 +17,7 @@ from cpython cimport PyObject, Py_INCREF, Py_DECREF#, PyMem_Malloc, PyMem_Free from libc.stdlib cimport malloc, free +from libc.string cimport memcpy, strdup from efl cimport Eina_Bool, const_Eina_List, eina_list_append, const_void from efl.c_eo cimport Eo as cEo from efl.c_eo cimport eo_init, eo_shutdown, eo_del, eo_unref, eo_wref_add, eo_add, Eo_Class @@ -37,46 +38,85 @@ cdef int PY_REFCOUNT(object o): cdef unicode _touni(char* s): + """ + + Converts a char * to a python string object + + Note: Remember to free the char * when it's no longer needed. + + """ return s.decode('UTF-8', 'strict') if s else None cdef unicode _ctouni(const_char *s): + """ + + Converts a const_char * to a python string object + + Note: Remember to free the const_char * when it's no longer needed. + + """ return s.decode('UTF-8', 'strict') if s else None -cdef char *_fruni(s): - cdef char *c_string +cdef char *_fruni(object s): + """ + + Converts a python string object to a char * + + Note: Remember to free the char * when it's no longer needed. + + """ + cdef: + char *c_string + str string + unicode unistr + if s is None: return NULL if isinstance(s, unicode): - string = s.encode('UTF-8') - # XXX: We lose reference here - c_string = string + unistr = s + string = unistr.encode('UTF-8') + return strdup(string) elif isinstance(s, str): - c_string = s - # XXX: Reference is lost unless the user keeps the string object around + return strdup(s) else: raise TypeError("Expected str or unicode object, got %s" % (type(s).__name__)) - return c_string -cdef const_char *_cfruni(s): - cdef const_char *c_string +cdef const_char *_cfruni(object s): + """ + + Converts a python string object to a const_char * + + Note: Remember to free the const_char * when it's no longer needed. + + """ + cdef: + const_char *c_string + str string + unicode unistr + if s is None: return NULL if isinstance(s, unicode): - string = s.encode('UTF-8') - # XXX: We lose reference here - c_string = string + unistr = s + string = unistr.encode('UTF-8') + return strdup(string) elif isinstance(s, str): - c_string = s - # XXX: Reference is lost unless the user keeps the string object around + return strdup(s) else: raise TypeError("Expected str or unicode object, got %s" % (type(s).__name__)) - return c_string -cdef convert_array_of_strings_to_python_list(char **array, int array_length): +cdef list convert_array_of_strings_to_python_list(char **array, int array_length): + """ + + Converts an array of strings to a python list. + + Note: Remember to free the array when it's no longer needed. + + """ cdef char *string ret = [] @@ -89,24 +129,37 @@ cdef convert_array_of_strings_to_python_list(char **array, int array_length): cdef const_char ** convert_python_list_strings_to_array_of_strings(list strings): + """ + + Converts a python list to an array of strings. + + Note: Remember to free the array when it's no longer needed. + + """ cdef: - const_char **lst + const_char **array + unsigned int arr_len = len(strings) const_char *string - int count = len(strings) + unsigned int str_len + unsigned int i - lst = malloc(count * sizeof(const_char*)) - for i in range(count): + array = malloc(arr_len * sizeof(const_char*)) + if not array: + raise MemoryError() + + for i in range(arr_len): string = _cfruni(strings[i]) - str_len = len(strings[i]) - lst[i] = malloc(str_len + 1) - memcpy(lst[i], string, str_len + 1) - # Note: Always make sure that the array is freed at the other end. - return lst + str_len = len(string) + array[i] = malloc(str_len + 1) + memcpy(array[i], string, str_len + 1) + # Note: Always make sure that the array is freed at the other end + return array -cdef convert_eina_list_strings_to_python_list(const_Eina_List *lst): - cdef const_char *s - ret = [] +cdef list convert_eina_list_strings_to_python_list(const_Eina_List *lst): + cdef: + const_char *s + list ret = [] while lst: s = lst.data if s != NULL: @@ -122,7 +175,7 @@ cdef Eina_List * convert_python_list_strings_to_eina_list(strings): return lst -cdef _object_list_to_python(const_Eina_List *lst): +cdef list _object_list_to_python(const_Eina_List *lst): ret = [] while lst: ret.append(object_from_instance(lst.data)) diff --git a/include/efl.eo.pxd b/include/efl.eo.pxd index 23338a5..e3a7c3b 100644 --- a/include/efl.eo.pxd +++ b/include/efl.eo.pxd @@ -20,10 +20,6 @@ from efl.c_eo cimport Eo_Class from efl cimport Eina_List, const_Eina_List from libc.string cimport const_char -cdef extern from "string.h": - void *memcpy(void *dst, void *src, int n) - char *strdup(char *str) - cdef class Eo(object): cdef cEo *obj cdef readonly data @@ -44,8 +40,8 @@ cdef char* _fruni(s) cdef unicode _ctouni(const_char *s) cdef const_char *_cfruni(s) -cdef convert_array_of_strings_to_python_list(char **array, int array_length) +cdef list convert_array_of_strings_to_python_list(char **array, int array_length) cdef const_char ** convert_python_list_strings_to_array_of_strings(list strings) -cdef convert_eina_list_strings_to_python_list(const_Eina_List *lst) +cdef list convert_eina_list_strings_to_python_list(const_Eina_List *lst) cdef Eina_List * convert_python_list_strings_to_eina_list(strings) -cdef _object_list_to_python(const_Eina_List *lst) +cdef list _object_list_to_python(const_Eina_List *lst)