You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by gs...@apache.org on 2012/05/11 23:39:16 UTC

svn commit: r1337387 - in /subversion/trunk/subversion/libsvn_ra_serf: ra_serf.h xml.c

Author: gstein
Date: Fri May 11 21:39:16 2012
New Revision: 1337387

URL: http://svn.apache.org/viewvc?rev=1337387&view=rev
Log:
Checkpoint commit; this new code is not (yet) invoked.

Begin some new work to revamp the XML parsing in ra_serf. This will
automate much of the state-based parsing that is occurring today,
along with rationalizing much of the pool/memory usage, and
simplifying a lot of the baton hierarchies that are in use today.

* subversion/libsvn_ra_serf/ra_serf.h:
  (svn_ra_serf__xml_context_t): the new xml parsing context
  (svn_ra_serf__xml_estate_t): the new element/state baton
  (svn_ra_serf__xml_opened_t): callback for custom work when an
    element is opened
  (svn_ra_serf__xml_closed_t): callback for custom work when an
    element is closed
  (svn_ra_serf__xml_transition_t): transition table describe each of
    the state transitions based on parsing xml elements
  (svn_ra_serf__xml_context_create): new parse context creation func
  (svn_ra_serf__xml_gather_since): utility function to gather a bunch
    of stashed attributes from a hierarchy of elements
  (svn_ra_serf__xml_note): allows a custom open function to note
    additional data on its estate, for later retrieval by the close
  (svn_ra_serf__xml_state_pool): retrieves the estate's pool for
    storing content with this element's lifetime
  (svn_ra_serf__xml_cb_start): callback to give to an XML parser for
    open-element calls
  (svn_ra_serf__xml_cb_end): callback to give to an XML parser for
    close-element calls
  (svn_ra_serf__xml_cb_cdata): callback to give to an XML parser for
    cdata content
  (svn_ra_serf__define_ns): constify ATTRS and s/POOL/RESULT_POOL/
  (svn_ra_serf__expand_ns): constify NS_LIST

* subversion/libsvn_ra_serf/xml.c:
  (svn_ra_serf__xml_context_t): new definition
  (svn_ra_serf__xml_estate_t): new definition
  (svn_ra_serf__define_ns): constify ATTRS and rename the pool.
    constify various localvars. handle the empty prefix. use
    svn_boolean_t. add a comment about NS chaining.
  (svn_ra_serf__expand_ns): constify NS_LIST and the NS localvar
  (xes_pool): helper to find a pool for certain allocations
  (ensure_pool): helper to ensure XES->STATE_POOL exists
  (svn_ra_serf__xml_context_create): new implementation
  (svn_ra_serf__xml_gather_since): new skeleton implementation
  (svn_ra_serf__xml_note): new skeleton implementation
  (svn_ra_serf__xml_state_pool): new implementation
  (svn_ra_serf__xml_cb_start): new draft implementation
  (svn_ra_serf__xml_cb_end): new draft implementation
  (svn_ra_serf__xml_cb_cdata): new implementation

Modified:
    subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
    subversion/trunk/subversion/libsvn_ra_serf/xml.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h?rev=1337387&r1=1337386&r2=1337387&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/ra_serf.h Fri May 11 21:39:16 2012
@@ -608,6 +608,136 @@ struct svn_ra_serf__xml_parser_t {
   apr_off_t read_size; /* Number of bytes read from response */
 };
 
+
+/* v2 of the XML parsing functions  */
+
+/* The XML parsing context.  */
+typedef struct svn_ra_serf__xml_context_t svn_ra_serf__xml_context_t;
+
+
+/* An opaque structure for the XML parse element/state.  */
+typedef struct svn_ra_serf__xml_estate_t svn_ra_serf__xml_estate_t;
+
+/* Called just after the parser moves into ENTERED_STATE.  */
+typedef svn_error_t *
+(*svn_ra_serf__xml_opened_t)(svn_ra_serf__xml_estate_t *xes,
+                             void *baton,
+                             int entered_state,
+                             apr_pool_t *scratch_pool);
+
+
+/* Called just before the parser leaves LEAVING_STATE.
+
+   If cdata collection was enabled for this state, then CDATA will be
+   non-NULL and contain the collected cdata.
+
+   If attribute collection was enabled for this state, then ATTRS will
+   contain the attributes collected for this element only. Use
+   svn_ra_serf__xml_gather_since() to gather up data from outer states.
+
+   Temporary allocations may be made in SCRATCH_POOL.  */
+typedef svn_error_t *
+(*svn_ra_serf__xml_closed_t)(svn_ra_serf__xml_estate_t *xes,
+                             void *baton,
+                             int leaving_state,
+                             const svn_string_t *cdata,
+                             apr_hash_t *attrs,
+                             apr_pool_t *scratch_pool);
+
+
+/* State transition table.
+
+   When the XML Context is constructed, it is in state 0. User states are
+   positive integers.
+
+   In a list of transitions, use { 0 } to indicate the end. Specifically,
+   the code looks for NS == NULL.
+
+   ### more docco
+*/
+typedef struct svn_ra_serf__xml_transition_t {
+  /* This transition applies when in this state  */
+  int from_state;
+
+  /* And when this tag is observed  */
+  const char *ns;
+  const char *name;
+
+  /* Moving to this state  */
+  int to_state;
+
+  /* Should the cdata of NAME be collected?  */
+  svn_boolean_t collect_cdata;
+
+  /* Which attributes of NAME should be collected? Terminate with NULL.
+     Maximum of 10 attributes may be collected. Note that attribute
+     namespaces are ignored at this time.
+
+     Attribute names beginning with "?" are optional. Other names must
+     exist on the element, or a parse error will be raised.  */
+  const char *collect_attrs[11];
+
+  /* When NAME is opened, should the callback be invoked?  */
+  svn_boolean_t custom_open;
+
+  /* When NAME is closed, should the callback be invoked?  */
+  svn_boolean_t custom_close;
+
+} svn_ra_serf__xml_transition_t;
+
+
+svn_ra_serf__xml_context_t *
+svn_ra_serf__xml_context_create(
+  const svn_ra_serf__xml_transition_t *ttable,
+  svn_ra_serf__xml_opened_t opened_cb,
+  svn_ra_serf__xml_closed_t closed_cb,
+  void *baton,
+  apr_pool_t *result_pool);
+
+
+/* Allocated within XES->STATE_POOL. Changes are not allowd. Make a deep
+   copy, as appropriate.  */
+apr_hash_t *
+svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
+                              int stop_state);
+
+
+/* ### maybe make value an svn_string_t * ?  */
+void
+svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
+                      const char *name,
+                      const char *value);
+
+
+/* Returns XES->STATE_POOL for allocating structures that should live
+   as long as the state identified by XES.  */
+apr_pool_t *
+svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes);
+
+
+/* Any XML parser may be used. When an opening tag is seen, call this
+   function to feed the information into XMLCTX.  */
+svn_error_t *
+svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *raw_name,
+                          const char *const *attrs);
+
+
+/* When a close tag is seen, call this function to feed the information
+   into XMLCTX.  */
+svn_error_t *
+svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+                        const char *raw_name);
+
+
+/* When cdata is parsed by the wrapping XML parser, call this function to
+   feed the cdata into the XMLCTX.  */
+svn_error_t *
+svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *data,
+                          apr_size_t len);
+
+
 /*
  * Parses a server-side error message into a local Subversion error.
  */
@@ -814,12 +944,12 @@ svn_ra_serf__add_cdata_len_buckets(serf_
  * Look up the @a attrs array for namespace definitions and add each one
  * to the @a ns_list of namespaces.
  *
- * New namespaces will be allocated in @a pool.
+ * New namespaces will be allocated in RESULT_POOL.
  */
 void
 svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
-                       const char **attrs,
-                       apr_pool_t *pool);
+                       const char *const *attrs,
+                       apr_pool_t *result_pool);
 
 /*
  * Look up @a name in the @a ns_list list for previously declared namespace
@@ -830,7 +960,7 @@ svn_ra_serf__define_ns(svn_ra_serf__ns_t
  */
 void
 svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
-                       svn_ra_serf__ns_t *ns_list,
+                       const svn_ra_serf__ns_t *ns_list,
                        const char *name);
 
 

Modified: subversion/trunk/subversion/libsvn_ra_serf/xml.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/xml.c?rev=1337387&r1=1337386&r2=1337387&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/xml.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/xml.c Fri May 11 21:39:16 2012
@@ -37,47 +37,114 @@
 #include "svn_config.h"
 #include "svn_delta.h"
 #include "svn_path.h"
+
 #include "svn_private_config.h"
+#include "private/svn_string_private.h"
 
 #include "ra_serf.h"
 
 
+struct svn_ra_serf__xml_context_t {
+  /* Current state information.  */
+  svn_ra_serf__xml_estate_t *current;
+
+  /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be
+     closed before looking for transitions from CURRENT->STATE.  */
+  svn_ra_serf__dav_props_t waiting;
+
+  /* The transition table.  */
+  const svn_ra_serf__xml_transition_t *ttable;
+
+  /* The callback information.  */
+  svn_ra_serf__xml_opened_t opened_cb;
+  svn_ra_serf__xml_closed_t closed_cb;
+  void *baton;
+
+  /* Linked list of free states.  */
+  svn_ra_serf__xml_estate_t *free_states;
+
+  apr_pool_t *scratch_pool;
+
+};
+
+struct svn_ra_serf__xml_estate_t {
+  /* The current state value.  */
+  int state;
+
+  /* The xml tag that opened this state. Waiting for the tag to close.  */
+  svn_ra_serf__dav_props_t tag;
+
+  /* Should the CLOSED_CB function be called for custom processing when
+     this tag is closed?  */
+  svn_boolean_t custom_close;
+
+  /* A pool may be constructed for this state.  */
+  apr_pool_t *state_pool;
+
+  /* The namespaces extent for this state/element. This will start with
+     the parent's NS_LIST, and we will push new namespaces into our
+     local list. The parent will be unaffected by our locally-scoped data. */
+  svn_ra_serf__ns_t *ns_list;
+
+  /* Any collected attribute values. char * -> svn_string_t *. May be NULL
+     if no attributes have been collected.  */
+  apr_hash_t *attrs;
+
+  /* Any collected cdata. May be NULL if no cdata is being collected.  */
+  svn_stringbuf_t *cdata;
+
+  /* Previous/outer state.  */
+  svn_ra_serf__xml_estate_t *prev;
+
+};
+
+
 void
 svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
-                       const char **attrs,
-                       apr_pool_t *pool)
+                       const char *const *attrs,
+                       apr_pool_t *result_pool)
 {
-  const char **tmp_attrs = attrs;
+  const char *const *tmp_attrs = attrs;
 
-  while (*tmp_attrs)
+  for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2)
     {
       if (strncmp(*tmp_attrs, "xmlns", 5) == 0)
         {
-          svn_ra_serf__ns_t *new_ns, *cur_ns;
-          int found = 0;
+          const svn_ra_serf__ns_t *cur_ns;
+          svn_boolean_t found = FALSE;
+          const char *prefix;
+
+          /* The empty prefix, or a named-prefix.  */
+          if (tmp_attrs[0][5] == ':')
+            prefix = &tmp_attrs[0][6];
+          else
+            prefix = "";
 
           /* Have we already defined this ns previously? */
           for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
             {
-              if (strcmp(cur_ns->namespace, tmp_attrs[0] + 6) == 0)
+              if (strcmp(cur_ns->namespace, prefix) == 0)
                 {
-                  found = 1;
+                  found = TRUE;
                   break;
                 }
             }
 
           if (!found)
             {
-              new_ns = apr_palloc(pool, sizeof(*new_ns));
-              new_ns->namespace = apr_pstrdup(pool, tmp_attrs[0] + 6);
-              new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
+              svn_ra_serf__ns_t *new_ns;
 
+              new_ns = apr_palloc(result_pool, sizeof(*new_ns));
+              new_ns->namespace = apr_pstrdup(result_pool, prefix);
+              new_ns->url = apr_pstrdup(result_pool, tmp_attrs[1]);
+
+              /* Push into the front of NS_LIST. Parent states will point
+                 to later in the chain, so will be unaffected by
+                 shadowing/other namespaces pushed onto NS_LIST.  */
               new_ns->next = *ns_list;
-
               *ns_list = new_ns;
             }
         }
-      tmp_attrs += 2;
     }
 }
 
@@ -87,7 +154,7 @@ svn_ra_serf__define_ns(svn_ra_serf__ns_t
  */
 void
 svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
-                       svn_ra_serf__ns_t *ns_list,
+                       const svn_ra_serf__ns_t *ns_list,
                        const char *name)
 {
   const char *colon;
@@ -96,7 +163,7 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_
   colon = strchr(name, ':');
   if (colon)
     {
-      svn_ra_serf__ns_t *ns;
+      const svn_ra_serf__ns_t *ns;
 
       prop_name.namespace = NULL;
 
@@ -322,3 +389,281 @@ void svn_ra_serf__xml_pop_state(svn_ra_s
   cur_state->prev = parser->free_state;
   parser->free_state = cur_state;
 }
+
+
+/* Return a pool for XES to use for self-alloc (and other specifics).  */
+static apr_pool_t *
+xes_pool(const svn_ra_serf__xml_estate_t *xes)
+{
+  /* Move up through parent states looking for one with a pool. This
+     will always terminate since the initial state has a pool.  */
+  while (xes->state_pool == NULL)
+    xes = xes->prev;
+  return xes->state_pool;
+}
+
+
+static void
+ensure_pool(svn_ra_serf__xml_estate_t *xes)
+{
+  if (xes->state_pool == NULL)
+    xes->state_pool = svn_pool_create(xes_pool(xes));
+}
+
+
+svn_ra_serf__xml_context_t *
+svn_ra_serf__xml_context_create(
+  const svn_ra_serf__xml_transition_t *ttable,
+  svn_ra_serf__xml_opened_t opened_cb,
+  svn_ra_serf__xml_closed_t closed_cb,
+  void *baton,
+  apr_pool_t *result_pool)
+{
+  svn_ra_serf__xml_context_t *xmlctx;
+  svn_ra_serf__xml_estate_t *xes;
+
+  xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx));
+  xmlctx->ttable = ttable;
+  xmlctx->opened_cb = opened_cb;
+  xmlctx->closed_cb = closed_cb;
+  xmlctx->baton = baton;
+  xmlctx->scratch_pool = svn_pool_create(result_pool);
+
+  xes = apr_pcalloc(result_pool, sizeof(*xes));
+  /* XES->STATE == 0  */
+  xes->ns_list = xmlctx->current->ns_list;
+
+  /* Child states may use this pool to allocate themselves. If a child
+     needs to collect information, then it will construct a subpool and
+     will use that to allocate itself and its collected data.  */
+  xes->state_pool = result_pool;
+
+  xmlctx->current = xes;
+
+  return xmlctx;
+}
+
+
+apr_hash_t *
+svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
+                              int stop_state)
+{
+  apr_hash_t *data;
+
+  ensure_pool(xes);
+
+  data = apr_hash_make(xes->state_pool);
+
+  /* ### gather data  */
+
+  return data;
+}
+
+
+void
+svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
+                      const char *name,
+                      const char *value)
+{
+  ensure_pool(xes);
+
+  /* ### copy into attrs  */
+}
+
+
+apr_pool_t *
+svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
+{
+  /* If they asked for a pool, then ensure that we have one to provide.  */
+  ensure_pool(xes);
+
+  return xes->state_pool;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *raw_name,
+                          const char *const *attrs)
+{
+  svn_ra_serf__xml_estate_t *current = xmlctx->current;
+  svn_ra_serf__dav_props_t name;
+  const svn_ra_serf__xml_transition_t *scan;
+  apr_pool_t *new_pool;
+  svn_ra_serf__xml_estate_t *new_xes;
+
+  /* If we're waiting for an element to close, then just ignore all
+     other element-opens.  */
+  if (xmlctx->waiting.namespace != NULL)
+    return SVN_NO_ERROR;
+
+#if 0
+  /* ### we need a lazy pool construction  */
+  svn_ra_serf__define_ns(&current->ns_list, attrs, ... );
+#endif
+
+  svn_ra_serf__expand_ns(&name, current->ns_list, raw_name);
+
+  for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
+    {
+      if (scan->from_state != current->state)
+        continue;
+
+      if (strcmp(name.name, scan->name) == 0
+          && strcmp(name.namespace, scan->ns) == 0)
+        break;
+    }
+  if (scan->ns == NULL)
+    {
+      xmlctx->waiting = name;
+      /* ### return?  */
+      return SVN_NO_ERROR;
+    }
+
+  /* We should not be told to collect cdata if the closed_cb will not
+     be called.  */
+  SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close);
+
+  /* Found a transition. Make it happen.  */
+
+  /* ### todo. push state  */
+
+  /* ### how to use free states?  */
+  /* This state should be allocated in the extent pool. If we will be
+     collecting information for this state, then construct a subpool.  */
+  new_pool = xes_pool(current);
+  if (scan->collect_cdata || scan->collect_attrs[0])
+    {
+      new_pool = svn_pool_create(new_pool);
+
+      /* Prep the new state.  */
+      new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
+      new_xes->state_pool = new_pool;
+
+      /* If we're supposed to collect cdata, then set up a buffer for
+         this. The existence of this buffer will instruct our cdata
+         callback to collect the cdata.  */
+      if (scan->collect_cdata)
+        new_xes->cdata = svn_stringbuf_create_empty(new_pool);
+
+      if (scan->collect_attrs)
+        {
+          new_xes->attrs = apr_hash_make(new_pool);
+          /* ### fill the hash  */
+
+          /* ### svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+                                   NULL, "Missing XML attribute: %s"
+                                   name);
+          */
+
+          /* ### potentially optimize away the subpool if none of the
+             ### attributes are present. subpools are cheap, tho...  */
+        }
+    }
+  else
+    {
+      /* Prep the new state.  */
+      new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
+      /* STATE_POOL remains NULL.  */
+    }
+
+  /* Some basic copies to set up the new estate.  */
+  new_xes->state = scan->to_state;
+  new_xes->tag = name;
+  new_xes->custom_close = scan->custom_close;
+
+  /* Start with the parent's namespace set.  */
+  new_xes->ns_list = current->ns_list;
+
+  /* The new state is prepared. Make it current.  */
+  new_xes->prev = current;
+  xmlctx->current = new_xes;
+
+  if (scan->custom_open)
+    SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
+                              new_xes->state, xmlctx->scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
+                        const char *raw_name)
+{
+  svn_ra_serf__xml_estate_t *xes = xmlctx->current;
+  svn_ra_serf__dav_props_t name;
+
+  svn_ra_serf__expand_ns(&name, xes->ns_list, raw_name);
+
+  if (xmlctx->waiting.namespace != NULL)
+    {
+      /* If this element is not the closer, then keep waiting... */
+      if (strcmp(name.name, xmlctx->waiting.name) != 0
+          || strcmp(name.namespace, xmlctx->waiting.namespace) != 0)
+        return SVN_NO_ERROR;
+
+      /* Found it. Stop waiting, and go back for more.  */
+      xmlctx->waiting.namespace = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  /* ### more work needed here.  */
+  if (strcmp(name.name, xes->tag.name) != 0
+      || strcmp(name.namespace, xes->tag.namespace) != 0)
+    {
+      /* ### how the heck do we have a closure for a tag that we were
+         ### not expecting? if we saw an unknown (open) tag, then we should
+         ### be waiting for it.  */
+      /* ### raise a parse error?  */
+      /* ###   SVN_ERR_RA_DAV_MALFORMED_DATA  */
+      return SVN_NO_ERROR;
+    }
+
+  if (xes->custom_close)
+    {
+      const svn_string_t *cdata;
+
+      cdata = svn_stringbuf__morph_into_string(xes->cdata);
+#ifdef SVN_DEBUG
+      /* We might toss the pool holding this structure, but it could also
+         be a parent pool. In any case, for safety's sake, disable the
+         stringbuf against future Badness.  */
+      xes->cdata->pool = NULL;
+#endif
+      SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state,
+                                cdata, xes->attrs,
+                                xmlctx->scratch_pool));
+    }
+
+  /* Pop the state.  */
+  /* ### not everything should go on the free state list. XES may go
+     ### away with the state pool.  */
+  xmlctx->current = xes->prev;
+  xes->prev = xmlctx->free_states;
+  xmlctx->free_states = xes;
+
+  /* If there is a STATE_POOL, then toss it. This will get rid of as much
+     memory as possible. Potentially the XES (if we didn't create a pool
+     right away, then XES may be in a parent pool).  */
+  if (xes->state_pool)
+    svn_pool_destroy(xes->state_pool);
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
+                          const char *data,
+                          apr_size_t len)
+{
+  /* If we're collecting cdata, but NOT waiting for a closing tag
+     (ie. not within an unknown tag), then copy the cdata.  */
+  if (xmlctx->current->cdata != NULL
+      && xmlctx->waiting.namespace == NULL)
+    svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
+
+  return SVN_NO_ERROR;
+}
+