[GRASS-SVN] r66236 - grass/trunk/lib/python/pygrass/vector

svn_grass at osgeo.org svn_grass at osgeo.org
Tue Sep 15 13:10:09 PDT 2015


Author: huhabla
Date: 2015-09-15 13:10:09 -0700 (Tue, 15 Sep 2015)
New Revision: 66236

Modified:
   grass/trunk/lib/python/pygrass/vector/geometry.py
Log:
pygrass vector: Implemented memory management for geometries to avoid memory leaks from line_cats and line_points structures


Modified: grass/trunk/lib/python/pygrass/vector/geometry.py
===================================================================
--- grass/trunk/lib/python/pygrass/vector/geometry.py	2015-09-15 15:38:42 UTC (rev 66235)
+++ grass/trunk/lib/python/pygrass/vector/geometry.py	2015-09-15 20:10:09 UTC (rev 66236)
@@ -259,34 +259,84 @@
     gtype = None
 
     def __init__(self, v_id=0, c_mapinfo=None, c_points=None, c_cats=None,
-                 table=None, writeable=False, is2D=True):
+                 table=None, writeable=False, is2D=True, free_points=False,
+                 free_cats=False):
+        """Constructor of a geometry object
+        
+            :param v_id:      The vector feature id
+            :param c_mapinfo: A pointer to the vector mapinfo structure
+            :param c_points:  A pointer to a libvect.line_pnts structure, this
+                              is optional, if not set an internal structure will 
+                              be allocated and free'd at object destruction
+            :param c_cats:    A pointer to a libvect.line_cats structure, this
+                              is optional, if not set an internal structure will 
+                              be allocated and free'd at object destruction
+            :param table:     The attribute table to select attributes for
+                              this feature
+            :param writeable: Not sure what this is for?
+            :param is2D:      If True this feature has two dimensions, False if
+                              this feature has three dimensions
+            :param free_points: Set this True if the provided c_points structure 
+                                should be free'd at object destruction, be aware
+                                that no other object should free them, otherwise
+                                you can expect a double free corruption segfault
+            :param free_cats:   Set this True if the provided c_cats structure 
+                                should be free'd at object destruction, be aware
+                                that no other object should free them, otherwise
+                                you can expect a double free corruption segfault
+        
+        """
         self.id = v_id  # vector id
         self.c_mapinfo = c_mapinfo
         self.is2D = (is2D if is2D is not None else
                      bool(libvect.Vect_is_3d(self.c_mapinfo) != 1))
 
+        # Set True if cats and points are allocated by this object
+        # to free the cats and points structures on destruction
+        self._free_points = False
+        self._free_cats = False
+        
         read = False
         # set c_points
         if c_points is None:
             self.c_points = ctypes.pointer(libvect.line_pnts())
+            self._free_points = True
             read = True
         else:
             self.c_points = c_points
+            self._free_points = free_points
 
         # set c_cats
         if c_cats is None:
             self.c_cats = ctypes.pointer(libvect.line_cats())
+            self._free_cats = free_cats
             read = True
         else:
             self.c_cats = c_cats
+            self._free_cats = True
 
         if self.id and self.c_mapinfo is not None and read:
             self.read()
 
         # set the attributes as last thing to do
-        self.attrs = None
+        self.attrs = None 
         if table is not None and self.cat is not None:
             self.attrs = Attrs(self.cat, table, writeable)
+    
+    def __del__(self):
+        """Take care of the allocated line_pnts and line_cats allocation
+        """
+        if self._free_points == True and self.c_points:
+            if self.c_points.contents.alloc_points > 0:
+                #print("G_free(points) [%i]"%(self.c_points.contents.alloc_points))
+                libgis.G_free(self.c_points.contents.x)
+                libgis.G_free(self.c_points.contents.y)
+                if self.c_points.contents.z:
+                    libgis.G_free(self.c_points.contents.z)
+        if self._free_cats == True and self.c_cats:
+            if self.c_cats.contents.alloc_cats > 0:
+                #print("G_free(cats) [%i]"%(self.c_cats.contents.alloc_cats))
+                libgis.G_free(self.c_cats.contents.cat)
 
     @property
     def cat(self):
@@ -314,11 +364,6 @@
             >>> pnt = Point(10, 100)
             >>> pnt.to_wkt()
             'POINT (10.0000000000000000 100.0000000000000000)'
-
-        .. warning::
-
-            Only ``POINT`` (2/3D) are supported, ``POINTM`` and ``POINT`` with:
-            ``XYZM`` are not supported yet.
         """
         return libvect.Vect_line_to_wkt(self.c_points, self.gtype, not self.is2D)
 
@@ -330,12 +375,6 @@
             >>> wkb = pnt.to_wkb()
             >>> len(wkb)
             21
-
-
-        .. warning::
-
-            Only ``POINT`` (2/3D) are supported, ``POINTM`` and ``POINT`` with:
-            ``XYZM`` are not supported yet.
         """
         size = ctypes.c_size_t()
         barray = libvect.Vect_line_to_wkb(self.c_points, self.gtype,
@@ -366,6 +405,19 @@
         >>> print(pnt)
         POINT Z (0.0000000000000000 0.0000000000000000 0.0000000000000000)
 
+
+        >>> c_points = ctypes.pointer(libvect.line_pnts())
+        >>> c_cats = ctypes.pointer(libvect.line_cats())
+        >>> p = Point(c_points = c_points, c_cats=c_cats)
+        >>> del p
+        
+        
+        >>> c_points = ctypes.pointer(libvect.line_pnts())
+        >>> c_cats = ctypes.pointer(libvect.line_cats())
+        >>> p = Point(c_points=c_points, c_cats=c_cats, free_points=True,
+        ...           free_cats=True)
+        >>> del p
+
     ..
     """
     # geometry type
@@ -1742,13 +1794,24 @@
 def read_next_line(c_mapinfo, table=None, writeable=False,
                    c_points=None, c_cats=None, is2D=True):
     """Return the next geometry feature of a vector map."""
+    
+    # Take care of good memory management
+    free_points = False
+    if c_points == None:
+        free_points = True
+        
+    free_cats = False
+    if c_cats == None:
+        free_cats = True
+    
     c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts())
     c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
     ftype, v_id, c_points, c_cats = c_read_next_line(c_mapinfo, c_points,
                                                      c_cats)
     return GV_TYPE[ftype]['obj'](v_id=v_id, c_mapinfo=c_mapinfo,
                                  c_points=c_points, c_cats=c_cats,
-                                 table=table, writeable=writeable, is2D=is2D)
+                                 table=table, writeable=writeable, is2D=is2D,
+                                 free_points=free_points, free_cats=free_cats)
 
 
 def c_read_line(feature_id, c_mapinfo, c_points, c_cats):
@@ -1768,6 +1831,15 @@
               c_points=None, c_cats=None, is2D=True):
     """Return a geometry object given the feature id and the c_mapinfo.
     """
+    # Take care of good memory management
+    free_points = False
+    if c_points == None:
+        free_points = True
+        
+    free_cats = False
+    if c_cats == None:
+        free_cats = True
+
     c_points = c_points if c_points else ctypes.pointer(libvect.line_pnts())
     c_cats = c_cats if c_cats else ctypes.pointer(libvect.line_cats())
     feature_id, ftype, c_points, c_cats = c_read_line(feature_id, c_mapinfo,
@@ -1775,9 +1847,10 @@
     if GV_TYPE[ftype]['obj'] is not None:
         return GV_TYPE[ftype]['obj'](v_id=feature_id, c_mapinfo=c_mapinfo,
                                      c_points=c_points, c_cats=c_cats,
-                                     table=table, writeable=writeable, is2D=is2D)
+                                     table=table, writeable=writeable, is2D=is2D,
+                                     free_points=free_points, 
+                                     free_cats=free_cats)
 
-
 if __name__ == "__main__":
     import doctest
     from grass.pygrass import utils



More information about the grass-commit mailing list