]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
pybind: Add decode_cstr helper function
authorDavid Coles <dcoles@gaikai.com>
Tue, 27 Oct 2015 20:32:44 +0000 (13:32 -0700)
committerDavid Coles <dcoles@gaikai.com>
Fri, 13 Nov 2015 01:26:34 +0000 (17:26 -0800)
This function attempts to decode a C-style string into a Python Unicode string.
It accepts an optional "size" parameter for the string length, otherwise it is
assumed that the string is NUL-terminated.

If the pointer is NULL, then this function returns None.

Signed-off-by: David Coles <dcoles@gaikai.com>
src/pybind/rados.py
src/pybind/rbd.py
src/test/pybind/test_rados.py

index 69431d229a4536fa6fe5d60c0684c00a315f99ac..75eb262a5a594d140de36d4c550294c729a19c07 100644 (file)
@@ -278,6 +278,7 @@ def cstr(val, encoding="utf-8"):
     Create a C-style string from a Python string
 
     :param str val: Python string
+    :param encoding: Encoding to use
     :rtype: c_char_p
     """
     if val is None:
@@ -290,6 +291,24 @@ def cstr(val, encoding="utf-8"):
         return c_char_p(val.encode(encoding))
 
 
+def decode_cstr(addr, size=-1, encoding="utf-8"):
+    """
+    Decode a C-style string into a Python string.
+
+    Return None if a the C string is a NULL pointer.
+
+    :param c_char_p addr: C-style string
+    :param int: String size (assume NUL-terminated if size is -1)
+    :param encoding: Encoding to use
+    :rtype: str or None
+    """
+    if not addr:
+        # NULL pointer
+        return None
+
+    return ctypes.string_at(addr, size).decode(encoding)
+
+
 class Rados(object):
     """librados python wrapper"""
     def require_state(self, *args):
@@ -411,7 +430,7 @@ Rados object in state %s." % self.state)
         # cretargs was allocated with fixed length; collapse return
         # list to eliminate any missing args
 
-        retargs = [a for a in cretargs if a is not None]
+        retargs = [decode_cstr(a) for a in cretargs if a is not None]
         self.parsed_args = args
         return retargs
 
@@ -447,7 +466,7 @@ Rados object in state %s." % self.state)
                                 (self.cluster, cstr(option), ret_buf,
                                 c_size_t(length)))
             if (ret == 0):
-                return ret_buf.value.decode("utf-8")
+                return decode_cstr(ret_buf)
             elif (ret == -errno.ENAMETOOLONG):
                 length = length * 2
             elif (ret == -errno.ENOENT):
@@ -501,7 +520,7 @@ Rados object in state %s." % self.state)
 
         if ret != 0:
             raise make_ex(ret, "error calling ping_monitor")
-        return my_outstr
+        return decode_cstr(my_outstr)
 
     def connect(self, timeout=0):
         """
@@ -704,7 +723,7 @@ Rados object in state %s." % self.state)
             else:
                 break
 
-        return [name for name in c_names.raw.decode("utf-8").split('\0') if len(name) > 0]
+        return [decode_cstr(name) for name in c_names.raw.split(b'\0') if len(name) > 0]
 
     def get_fsid(self):
         """
@@ -771,7 +790,7 @@ Rados object in state %s." % self.state)
 
         # copy returned memory (ctypes makes a copy, not a reference)
         my_outbuf = outbufp.contents[:(outbuflen.value)]
-        my_outs = outsp.contents[:(outslen.value)]
+        my_outs = decode_cstr(outsp.contents, outslen.value)
 
         # free callee's allocations
         if outbuflen.value:
@@ -800,7 +819,7 @@ Rados object in state %s." % self.state)
 
         # copy returned memory (ctypes makes a copy, not a reference)
         my_outbuf = outbufp.contents[:(outbuflen.value)]
-        my_outs = outsp.contents[:(outslen.value)]
+        my_outs = decode_cstr(outsp.contents, outslen.value)
 
         # free callee's allocations
         if outbuflen.value:
@@ -829,7 +848,7 @@ Rados object in state %s." % self.state)
 
         # copy returned memory (ctypes makes a copy, not a reference)
         my_outbuf = outbufp.contents[:(outbuflen.value)]
-        my_outs = outsp.contents[:(outslen.value)]
+        my_outs = decode_cstr(outsp.contents, outslen.value)
 
         # free callee's allocations
         if outbuflen.value:
@@ -888,7 +907,7 @@ class OmapIterator(Iterator):
             raise make_ex(ret, "error iterating over the omap")
         if key_.value is None:
             raise StopIteration()
-        key = key_.value.decode("utf-8")
+        key = decode_cstr(key_)
         val = None
         if val_.value is not None:
             val = ctypes.string_at(val_, len_)
@@ -930,9 +949,9 @@ class ObjectIterator(Iterator):
         if ret < 0:
             raise StopIteration()
 
-        key = None if key_.value is None else key_.value.decode("utf-8")
-        locator = None if locator_.value is None else locator_.value.decode("utf-8")
-        nspace = None if nspace_.value is None else nspace_.value.decode("utf-8")
+        key = decode_cstr(key_)
+        locator = decode_cstr(locator_)
+        nspace = decode_cstr(nspace_)
         return Object(self.ioctx, key, locator, nspace)
 
     def __del__(self):
@@ -969,7 +988,7 @@ class XattrIterator(Iterator):
 in '%s'" % self.oid)
         if name_.value is None:
             raise StopIteration()
-        name = ctypes.string_at(name_).decode("utf-8")
+        name = decode_cstr(name_)
         val = ctypes.string_at(val_, len_)
         return (name, val)
 
@@ -1025,7 +1044,7 @@ ioctx '%s'" % self.ioctx.name)
             elif (ret != -errno.ERANGE):
                 raise make_ex(ret, "rados_snap_get_name error")
             name_len = name_len * 2
-        snap = Snap(self.ioctx, name.value.decode("utf-8"), snap_id)
+        snap = Snap(self.ioctx, decode_cstr(name), snap_id)
         self.cur_snap = self.cur_snap + 1
         return snap
 
index b0bf552ebf462c7fd6698d801f0eed5932fa1ca9..ca45534511e4fa0cfd5a32a14bd6d4a80a3dc6aa 100644 (file)
@@ -20,7 +20,7 @@ import ctypes
 import errno
 import sys
 
-from rados import cstr
+from rados import cstr, decode_cstr
 
 ANONYMOUS_AUID = 0xffffffffffffffff
 ADMIN_AUID = 0
@@ -343,7 +343,7 @@ class RBD(object):
             elif ret != -errno.ERANGE:
                 raise make_ex(ret, 'error listing images')
 
-        return [name for name in c_names.raw.decode("utf-8").split('\0') if len(name) > 0]
+        return [decode_cstr(name) for name in c_names.raw.split(b'\0') if len(name) > 0]
 
     def remove(self, ioctx, name):
         """
@@ -1025,7 +1025,7 @@ written." % (self.name, ret, length))
         cookies = [cookie.decode("utf-8") for cookie in c_cookies.raw[:cookies_size.value - 1].split(b'\0')]
         addrs = [addr.decode("utf-8") for addr in c_addrs.raw[:addrs_size.value - 1].split(b'\0')]
         return {
-            'tag'       : c_tag.value.decode("utf-8"),
+            'tag'       : decode_cstr(c_tag),
             'exclusive' : exclusive.value == 1,
             'lockers'   : list(zip(clients, cookies, addrs)),
             }
@@ -1125,7 +1125,7 @@ class SnapIterator(Iterable):
             yield {
                 'id'   : self.snaps[i].id,
                 'size' : self.snaps[i].size,
-                'name' : self.snaps[i].name.decode("utf-8"),
+                'name' : decode_cstr(self.snaps[i].name),
                 }
 
     def __del__(self):
index ebc4e8fcdee652259c14c569006e2952be4295c6..1991d0c815a66061dc7208f0fa704baa35c35d21 100644 (file)
@@ -660,6 +660,6 @@ class TestCommand(object):
                                                timeout=30)
         eq(ret, 0)
         assert len(err) > 0
-        out = json.loads(err.decode("utf-8"))
+        out = json.loads(err)
         eq(out['blocksize'], cmd['size'])
         eq(out['bytes_written'], cmd['count'])