]> granicus.if.org Git - postgis/commitdiff
Update caching code to be more generic, using call-backs to allocate/build/free speci...
authorPaul Ramsey <pramsey@cleverelephant.ca>
Mon, 18 Jun 2012 17:12:22 +0000 (17:12 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Mon, 18 Jun 2012 17:12:22 +0000 (17:12 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@9928 b70326c6-7e19-0410-871a-916f4a2858ee

libpgcommon/lwgeom_cache.c
libpgcommon/lwgeom_cache.h
postgis/lwgeom_geos_prepared.c
postgis/lwgeom_geos_prepared.h
postgis/lwgeom_rtree.c
postgis/lwgeom_rtree.h

index 59063715b2f88eb4ee122012c581609b6c12d0b2..a4f55dbe29bfe02c1bb3dc0407b3fee8a780a2aa 100644 (file)
@@ -119,6 +119,7 @@ GetPROJ4SRSCache(FunctionCallInfoData* fcinfo)
 * GeomCache entry from the generic cache if one exists.
 * If it doesn't exist, make a new empty one and return it.
 */
+#if 0
 GeomCache*            
 GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry)
 {
@@ -154,8 +155,112 @@ GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry)
                
        return cache;
 }
+#endif
 
 
+GeomCache*            
+GetGeomCache2(FunctionCallInfoData* fcinfo, const GeomCacheMethods* cache_methods, const GSERIALIZED* g1, const GSERIALIZED* g2)
+{
+       GeomCache* cache;
+       int cache_hit = 0;
+       MemoryContext old_context;
+       const GSERIALIZED *geom;
+       GenericCacheCollection* generic_cache = GetGenericCacheCollection(fcinfo);
+       int entry_number = cache_methods->entry_number;
+       
+       Assert(entry_number >= 0);
+       Assert(entry_number < NUM_CACHE_ENTRIES);
+       
+       cache = (GeomCache*)(generic_cache->entry[entry_number]);
+       
+       if ( ! cache )
+       {
+               old_context = MemoryContextSwitchTo(FIContext(fcinfo));
+               /* Allocate in the upper context */
+               cache = cache_methods->GeomCacheAllocator();
+               MemoryContextSwitchTo(old_context);
+               /* Store the pointer in GenericCache */
+               cache->type = entry_number;
+               generic_cache->entry[entry_number] = (GenericCache*)cache;
+       }       
+
+       /* Cache hit on the first argument */
+       if ( g1 &&
+            cache->argnum != 2 &&
+            cache->geom1_size == VARSIZE(g1) &&
+            memcmp(cache->geom1, g1, cache->geom1_size) == 0 )
+       {
+               cache_hit = 1;
+               geom = g1;
+
+       }
+       /* Cache hit on second argument */
+       else if ( g2 &&
+                 cache->argnum != 1 &&
+                 cache->geom2_size == VARSIZE(g2) &&
+                 memcmp(cache->geom2, g2, cache->geom2_size) == 0 )
+       {
+               cache_hit = 2;
+               geom = g2;
+       }
+       /* No cache hit. If we have a tree, free it. */
+       else
+       {
+               cache_hit = 0;
+               if ( cache->argnum )
+               {
+                       cache_methods->GeomIndexFreer(cache);
+                       cache->argnum = 0;
+               }
+       }
+
+       /* Cache hit, but no tree built yet, build it! */
+       if ( cache_hit && ! cache->argnum )
+       {
+               int rv;
+               LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
+
+               /* Can't build a tree on a NULL or empty */
+               if ( (!lwgeom) || lwgeom_is_empty(lwgeom) )
+                       return NULL;
+
+               old_context = MemoryContextSwitchTo(FIContext(fcinfo));
+               rv = cache_methods->GeomIndexBuilder(lwgeom, cache);
+               MemoryContextSwitchTo(old_context);
+               cache->argnum = cache_hit;
+
+               /* Something went awry in the tree build phase */
+               if ( ! rv )
+               {
+                       cache->argnum = 0;
+                       return NULL;
+               }
+
+       }
+
+       /* We have a hit and a calculated tree, we're done */
+       if ( cache_hit && cache->argnum )
+               return cache;
+
+       /* Argument one didn't match, so copy the new value in. */
+       if ( g1 && cache_hit != 1 )
+       {
+               if ( cache->geom1 ) pfree(cache->geom1);
+               cache->geom1_size = VARSIZE(g1);
+               cache->geom1 = MemoryContextAlloc(FIContext(fcinfo), cache->geom1_size);
+               memcpy(cache->geom1, g1, cache->geom1_size);
+       }
+       /* Argument two didn't match, so copy the new value in. */
+       if ( g2 && cache_hit != 2 )
+       {
+               if ( cache->geom2 ) pfree(cache->geom2);
+               cache->geom2_size = VARSIZE(g2);
+               cache->geom2 = MemoryContextAlloc(FIContext(fcinfo), cache->geom2_size);
+               memcpy(cache->geom2, g2, cache->geom2_size);
+       }
+
+       return NULL;
+}
 
 
 
@@ -164,6 +269,7 @@ GetGeomCache(FunctionCallInfoData* fcinfo, int cache_entry)
 * and the geometry values. Checks for a cache, checks for cache hits, 
 * returns a built tree if one exists. 
 */
+#if 0
 void* 
 GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer index_free, const GSERIALIZED* geom1, const GSERIALIZED* geom2, int* argnum)
 {
@@ -258,7 +364,7 @@ GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder ind
        return NULL;
 }
 
-
+#endif
 
 /**
 * Builder, freeer and public accessor for cached CIRC_NODE trees
@@ -266,43 +372,68 @@ GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder ind
 static int
 CircTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
 {
+       CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache;
        CIRC_NODE* tree = lwgeom_calculate_circ_tree(lwgeom);
-       if ( cache->index )
+
+       if ( circ_cache->index )
        {
-               circ_tree_free((CIRC_NODE*)(cache->index));
-               cache->index = 0;
+               circ_tree_free(circ_cache->index);
+               circ_cache->index = 0;
        }
        if ( ! tree )
                return LW_FAILURE;
        
-       cache->index = (void*)tree;
+       circ_cache->index = tree;
        return LW_SUCCESS;
 }
 
 static int
 CircTreeFreer(GeomCache* cache)
 {
-       CIRC_NODE* tree = (CIRC_NODE*)(cache->index);
-       if ( tree ) 
+       CircTreeGeomCache* circ_cache = (CircTreeGeomCache*)cache;
+       if ( circ_cache->index ) 
        {
-               circ_tree_free(tree);
-               cache->index = 0;
+               circ_tree_free(circ_cache->index);
+               circ_cache->index = 0;
+               circ_cache->argnum = 0;
        }
        return LW_SUCCESS;
 }
 
+static GeomCache*
+CircTreeAllocator(void)
+{
+       CircTreeGeomCache* cache = palloc(sizeof(CircTreeGeomCache));
+       memset(cache, 0, sizeof(CircTreeGeomCache));
+       return (GeomCache*)cache;
+}
+
+static GeomCacheMethods CircTreeCacheMethods =
+{
+       CIRC_CACHE_ENTRY,
+       CircTreeBuilder,
+       CircTreeFreer,
+       CircTreeAllocator
+};
+
 CIRC_NODE* 
-GetCircTree(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2, int* argnum_of_cache)
+GetCircTree(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum_of_cache)
 {
-       int argnum = 0;
-       CIRC_NODE* tree = NULL;
+       CircTreeGeomCache* cache = (CircTreeGeomCache*)GetGeomCache2(fcinfo, &CircTreeCacheMethods, g1, g2);
 
-       tree = (CIRC_NODE*)GetGeomIndex(fcinfo, CIRC_CACHE_ENTRY, CircTreeBuilder, CircTreeFreer, g1, g2, &argnum);
+       if ( ! cache )
+               return NULL;
 
        if ( argnum_of_cache )
-               *argnum_of_cache = argnum;
+               *argnum_of_cache = cache->argnum;
                
-       return tree;
+       return cache->index;
+}
+
+CircTreeGeomCache* 
+GetCircTreeGeomCache(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2)
+{
+       return (CircTreeGeomCache*)GetGeomCache2(fcinfo, &CircTreeCacheMethods, g1, g2);
 }
 
 
index 4bb2571cce354d5be8e92eb3e19fca3e9b364d25..83674fc863216f9a791f093eaf3da66253f64c73 100644 (file)
@@ -28,6 +28,7 @@
 #define RTREE_CACHE_ENTRY 2
 #define CIRC_CACHE_ENTRY 3
 #define RECT_CACHE_ENTRY 4
+
 #define NUM_CACHE_ENTRIES 5
 
 
 * argument the tree is built for.
 */
 typedef struct {
-       int            type;
-       GSERIALIZED*   geom1;
-       GSERIALIZED*   geom2;
-       size_t         geom1_size;
-       size_t         geom2_size;
-       int32          argnum;
-       MemoryContext  context_statement;
-       MemoryContext  context_callback;
-       void*          index;
+       int                         type;
+       GSERIALIZED*                geom1;
+       GSERIALIZED*                geom2;
+       size_t                      geom1_size;
+       size_t                      geom2_size;
+       int32                       argnum; 
 } GeomCache;
 
+typedef struct {
+       int                         type;       // <GeomCache>
+       GSERIALIZED*                geom1;      // 
+       GSERIALIZED*                geom2;      // 
+       size_t                      geom1_size; // 
+       size_t                      geom2_size; // 
+       int32                       argnum;     // </GeomCache>
+       CIRC_NODE*                  index;
+} CircTreeGeomCache;
+
 
 /* An entry in the PROJ4 SRS cache */
 typedef struct struct_PROJ4SRSCacheItem
@@ -81,21 +89,33 @@ PROJ4PortalCache;
 * geometry and return a tree structure for fast edge
 * access.
 */
+#if 0
 typedef int (*GeomIndexBuilder)(const LWGEOM* lwgeom, GeomCache* cache);
 typedef int (*GeomIndexFreer)(GeomCache* cache);
+typedef GeomCache* (*GeomCacheAllocator)(void);
+#endif
+
+typedef struct 
+{
+       int entry_number;
+       int (*GeomIndexBuilder)(const LWGEOM* lwgeom, GeomCache* cache);
+       int (*GeomIndexFreer)(GeomCache* cache);
+       GeomCache* (*GeomCacheAllocator)(void);
+} GeomCacheMethods;
 
 /* 
 * Cache retrieval functions
 */
-PROJ4PortalCache* GetPROJ4SRSCache(FunctionCallInfoData *fcinfo);
-GeomCache*        GetGeomCache(FunctionCallInfoData *fcinfo, int cache_entry);
-CIRC_NODE*        GetCircTree(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2, int* argnum_of_cache);
+PROJ4PortalCache*  GetPROJ4SRSCache(FunctionCallInfoData *fcinfo);
+GeomCache*         GetGeomCache2(FunctionCallInfoData *fcinfo, const GeomCacheMethods* cache_methods, const GSERIALIZED* g1, const GSERIALIZED* g2);
+CIRC_NODE*         GetCircTree(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum_of_cache);
+CircTreeGeomCache* GetCircTreeGeomCache(FunctionCallInfoData* fcinfo, const GSERIALIZED* g1, const GSERIALIZED* g2);
 
 /* 
 * Given candidate geometries, a builer function and an entry type, cache and/or return an
 * appropriate tree.
 */
-void* GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer tree_free, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum);
+//void* GetGeomIndex(FunctionCallInfoData* fcinfo, int cache_entry, GeomIndexBuilder index_build, GeomIndexFreer tree_free, const GSERIALIZED* g1, const GSERIALIZED* g2, int* argnum);
 
 
 #endif /* LWGEOM_CACHE_H_ */
index f722c6180d75e31f507bd4a636c456d82a32e555..0a613ea839f2f883b75ce8df9ca7d8f999b7c7b4 100644 (file)
@@ -281,7 +281,7 @@ DeletePrepGeomHashEntry(MemoryContext mcxt)
 static int
 PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache)
 {
-       PrepGeomCache* prepcache;
+       PrepGeomCache* prepcache = (PrepGeomCache*)cache;
        PrepGeomHashEntry* pghe;
        
        /*
@@ -293,14 +293,14 @@ PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache)
        /*
        * No callback entry for this statement context yet? Set it up
        */
-       if ( ! cache->context_callback )
+       if ( ! prepcache->context_callback )
        {
                PrepGeomHashEntry pghe;
-               cache->context_callback = MemoryContextCreate(T_AllocSetContext, 8192,
+               prepcache->context_callback = MemoryContextCreate(T_AllocSetContext, 8192,
                                             &PreparedCacheContextMethods,
-                                            cache->context_statement,
+                                            prepcache->context_statement,
                                             "PostGIS Prepared Geometry Context");
-               pghe.context = cache->context_callback;
+               pghe.context = prepcache->context_callback;
                pghe.geom = 0;
                pghe.prepared_geom = 0;
                AddPrepGeomHashEntry( pghe );           
@@ -310,19 +310,12 @@ PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache)
        * Hum, we shouldn't be asked to build a new cache on top of 
        * an existing one. Error.
        */
-       if ( cache->index )
+       if ( prepcache->argnum || prepcache->geom || prepcache->prepared_geom )
        {
                lwerror("PrepGeomCacheBuilder asked to build new prepcache where one already exists.");
                return LW_FAILURE;
        }
        
-       /* 
-       * Allocate a new index structure 
-       */
-       cache->index = MemoryContextAlloc(cache->context_statement, sizeof(PrepGeomCache));
-       memset(cache->index, 0, sizeof(PrepGeomCache));
-       prepcache = (PrepGeomCache*)(cache->index);
-       
        prepcache->geom = LWGEOM2GEOS( lwgeom );
        if ( ! prepcache->geom ) return LW_FAILURE;
        prepcache->prepared_geom = GEOSPrepare( prepcache->geom );
@@ -333,10 +326,10 @@ PrepGeomCacheBuilder(const LWGEOM *lwgeom, GeomCache *cache)
        * In order to find the objects we need to destroy, we keep
        * extra references in a global hash object.
        */
-       pghe = GetPrepGeomHashEntry(cache->context_callback);
+       pghe = GetPrepGeomHashEntry(prepcache->context_callback);
        if ( ! pghe )
        {
-               lwerror("PrepGeomCacheBuilder failed to find hash entry for context %p", cache->context_callback);
+               lwerror("PrepGeomCacheBuilder failed to find hash entry for context %p", prepcache->context_callback);
                return LW_FAILURE;
        }
        
@@ -360,7 +353,7 @@ static int
 PrepGeomCacheCleaner(GeomCache *cache)
 {
        PrepGeomHashEntry* pghe = 0;
-       PrepGeomCache* prepcache = (PrepGeomCache*)(cache->index);
+       PrepGeomCache* prepcache = (PrepGeomCache*)cache;
 
        if ( ! prepcache )
                return LW_FAILURE;
@@ -369,10 +362,10 @@ PrepGeomCacheCleaner(GeomCache *cache)
        * Clear out the references to the soon-to-be-freed GEOS objects 
        * from the callback hash entry
        */
-       pghe = GetPrepGeomHashEntry(cache->context_callback);
+       pghe = GetPrepGeomHashEntry(prepcache->context_callback);
        if ( ! pghe )
        {
-               lwerror("PrepGeomCacheCleaner failed to find hash entry for context %p", cache->context_callback);
+               lwerror("PrepGeomCacheCleaner failed to find hash entry for context %p", prepcache->context_callback);
                return LW_FAILURE;
        }
        pghe->geom = 0;
@@ -384,12 +377,32 @@ PrepGeomCacheCleaner(GeomCache *cache)
        POSTGIS_DEBUGF(3, "PrepGeomCacheFreeer: freeing %p argnum %d", prepcache, prepcache->argnum);
        GEOSPreparedGeom_destroy( prepcache->prepared_geom );
        GEOSGeom_destroy( (GEOSGeometry *)prepcache->geom );
-       pfree(cache->index);
-       cache->index = 0;
+       prepcache->argnum = 0;
+       prepcache->prepared_geom = 0;
+       prepcache->geom = 0;
        
        return LW_SUCCESS;
 }
 
+static GeomCache*
+PrepGeomCacheAllocator()
+{
+       PrepGeomCache* prepcache = palloc(sizeof(PrepGeomCache));
+       memset(prepcache, 0, sizeof(PrepGeomCache));
+       prepcache->context_statement = CurrentMemoryContext;
+       prepcache->type = PREP_CACHE_ENTRY;
+       return (GeomCache*)prepcache;
+}
+
+static GeomCacheMethods PrepGeomCacheMethods =
+{
+       PREP_CACHE_ENTRY,
+       PrepGeomCacheBuilder,
+       PrepGeomCacheCleaner,
+       PrepGeomCacheAllocator
+};
+
+
 /**
 * Given a couple potential geometries and a function
 * call context, return a prepared structure for one
@@ -404,11 +417,6 @@ PrepGeomCacheCleaner(GeomCache *cache)
 PrepGeomCache*
 GetPrepGeomCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1, GSERIALIZED* g2)
 {
-       int argnum = 0;
-       PrepGeomCache* prepcache = NULL;
-
-       prepcache = (PrepGeomCache*)GetGeomIndex(fcinfo, PREP_CACHE_ENTRY, PrepGeomCacheBuilder, PrepGeomCacheCleaner, g1, g2, &argnum);
-
-       return prepcache;
+       return (PrepGeomCache*)GetGeomCache2(fcinfo, &PrepGeomCacheMethods, g1, g2);
 }
 
index e100ba8febbe70481f2e53f7ecee9a2f9a4e9518..0fabf36bee746dee1f4ec8cc10fd11e3be914a1d 100644 (file)
@@ -47,7 +47,7 @@ typedef struct
 }
 PrepGeomCache;
 */
-
+/*
 typedef struct
 {
        int                           argnum;
@@ -55,6 +55,20 @@ typedef struct
        const GEOSGeometry            *geom;
 }
 PrepGeomCache;
+*/
+typedef struct {
+       int                         type;       // <GeomCache>
+       GSERIALIZED*                geom1;      // 
+       GSERIALIZED*                geom2;      // 
+       size_t                      geom1_size; // 
+       size_t                      geom2_size; // 
+       int32                       argnum;     // </GeomCache>
+       MemoryContext               context_statement;
+       MemoryContext               context_callback;
+       const GEOSPreparedGeometry* prepared_geom;
+       const GEOSGeometry*         geom;
+} PrepGeomCache;
+
 
 /*
 ** Get the current cache, given the input geometries.
index 7bef46bb739cda1923378c357e994e4b5b6482cd..1408c5e78e7b2c728a7ea24a8ad542103bfdf9ed 100644 (file)
@@ -305,10 +305,17 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
        LWMPOLY *mpoly;
        LWPOLY *poly;
        int nrings;
+       RTreeGeomCache* rtree_cache = (RTreeGeomCache*)cache;
        RTREE_POLY_CACHE* currentCache;
        
        if ( ! cache )
                return LW_FAILURE;
+
+       if ( rtree_cache->index )
+       {
+               lwerror("RTreeBuilder asked to build index where one already exists.");
+               return LW_FAILURE;
+       }
        
        if (lwgeom->type == MULTIPOLYGONTYPE)
        {
@@ -340,7 +347,7 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
                                i++;
                        }
                }
-               cache->index = (void*)currentCache;
+               rtree_cache->index = currentCache;
        }
        else if ( lwgeom->type == POLYGONTYPE )
        {
@@ -358,7 +365,7 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
                {
                        currentCache->ringIndices[i] = RTreeCreate(poly->rings[i]);
                }
-               cache->index = (void*)currentCache;
+               rtree_cache->index = currentCache;
        }
        else
        {
@@ -376,26 +383,45 @@ RTreeBuilder(const LWGEOM* lwgeom, GeomCache* cache)
 static int
 RTreeFreer(GeomCache* cache)
 {
+       RTreeGeomCache* rtree_cache = (RTreeGeomCache*)cache;
+       
        if ( ! cache )
                return LW_FAILURE;
        
-       if ( cache->index )
+       if ( rtree_cache->index )
        {
-               RTREE_POLY_CACHE* currentCache = (RTREE_POLY_CACHE*)(cache->index);
-               RTreeCacheClear(currentCache);
-               lwfree(currentCache);
-               cache->index = 0;
+               RTreeCacheClear(rtree_cache->index);
+               lwfree(rtree_cache->index);
+               rtree_cache->index = 0;
+               rtree_cache->argnum = 0;
        }
        return LW_SUCCESS;
 }
 
+static GeomCache*
+RTreeAllocator(void)
+{
+       RTreeGeomCache* cache = palloc(sizeof(RTreeGeomCache));
+       memset(cache, 0, sizeof(RTreeGeomCache));
+       return (GeomCache*)cache;
+}
+
+static GeomCacheMethods RTreeCacheMethods =
+{
+       RTREE_CACHE_ENTRY,
+       RTreeBuilder,
+       RTreeFreer,
+       RTreeAllocator
+};
+
 RTREE_POLY_CACHE*
 GetRtreeCache(FunctionCallInfoData* fcinfo, GSERIALIZED* g1)
 {
-       int argnum = 0;
+       RTreeGeomCache* cache = (RTreeGeomCache*)GetGeomCache2(fcinfo, &RTreeCacheMethods, g1, NULL);
        RTREE_POLY_CACHE* index = NULL;
 
-       index = (RTREE_POLY_CACHE*)GetGeomIndex(fcinfo, RTREE_CACHE_ENTRY, RTreeBuilder, RTreeFreer, g1, 0, &argnum);
+       if ( cache )
+               index = cache->index;
 
        return index;
 }
index bf1ae9d768028d04b4feae2612e38c22fa3ff129..12a986e81a35f84b3282302de9a18bde265b5353 100644 (file)
@@ -38,6 +38,19 @@ typedef struct
 }
 RTREE_POLY_CACHE;
 
+
+
+typedef struct {
+       int                         type;       // <GeomCache>
+       GSERIALIZED*                geom1;      // 
+       GSERIALIZED*                geom2;      // 
+       size_t                      geom1_size; // 
+       size_t                      geom2_size; // 
+       int32                       argnum;     // </GeomCache>
+       RTREE_POLY_CACHE*           index;
+} RTreeGeomCache;
+
+
 /**
 * Retrieves a collection of line segments given the root and crossing value. 
 */