Hi,
In my use-case I am dealing with multiple headers with the same key (e.g. Cookie), and need to modify or remove a specific header based on its key and value (e.g. remove a certain cookie header while leaving the rest in). There is no api in apr_tables that would allow me to remove a given header instance. apr_table_unset will remove all cookies. And I can't have my code remove a header from the apr_array_table_t array because that will render the internal hash index incorrect. Secondly, eventhough I can modify the value of a specific header by iterating over the apr_array_header_t, that would be inefficient because I wouldn't be able to use the internal index_first and index_last values. Therefore I have written three functions (patch files attached) and am hoping that the powers-that-be will be willing to review and roll them into the APR codeline.


1) apr_table_set_one (apr_table_t *t, const char *key, const char *oldVal, const char *newVal)
replaces value of header "key" and value "oldVal" with value "newVal". If "oldVal" is null, then the first occurance of the header is replaces (this is an optimization for situations where we know that only one header exists and don't care about its current value). If the header is not found, then it behaves like apr_table_add.


2) apr_table_setn_one(apr_table_t *t, const char *key, const char *oldVal, const char *newVal)
Same as apr_table_set_one exept that it doesn't make a copy of key and newVal.


3) apr_table_unset_one(apr_table_t *t, const char *key, const char *oldVal, const char *newVal)
Unsets header "key" with value "oldVal". If "oldVal" is null, then the first instance of the header (only) is unset (this is an optimization for situations where we know that only one header exists and don't care about its current value).


-regards,
sumeet
*** apr_tables.c        Fri Feb 13 01:38:34 2004
--- apr_tables++.c      Thu Apr 15 18:39:26 2004
***************
*** 1204,1206 ****
--- 1204,1304 ----
  
      apr_table_compress(a, flags);
  }
+ 
+ static void apr_table_set_one_internal(apr_table_t *t,
+                                        const char *key,
+                                        const char *oldVal,
+                                        const char *newVal,
+                                        int copy)
+ {
+     apr_table_entry_t *next_elt;
+     apr_table_entry_t *end_elt;
+     apr_table_entry_t *table_end;
+     apr_uint32_t checksum;
+     int hash;
+ 
+     COMPUTE_KEY_CHECKSUM(key, checksum);
+     hash = TABLE_HASH(key);
+     if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
+         t->index_first[hash] = t->a.nelts;
+         TABLE_SET_INDEX_INITIALIZED(t, hash);
+         goto add_new_elt;
+     }
+     next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];;
+     end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
+     table_end =((apr_table_entry_t *) t->a.elts) + t->a.nelts;
+ 
+     for (; next_elt <= end_elt; next_elt++) {
+         if ((checksum == next_elt->key_checksum) &&
+             !strcasecmp(next_elt->key, key) &&
+             (!oldVal || !strcmp(next_elt->val, oldVal))) {
+ 
+             /* Found An Existing Entry With The Same Key, So Overwrite It */
+             next_elt->val = copy ? apr_pstrdup(t->a.pool, newVal)
+                                  : (char *) newVal;
+             return;
+         }
+     }
+ 
+ add_new_elt:
+     t->index_last[hash] = t->a.nelts;
+     next_elt = (apr_table_entry_t *) table_push(t);
+     next_elt->key = copy ? apr_pstrdup(t->a.pool, key): (char *) key;
+     next_elt->val = copy ? apr_pstrdup(t->a.pool, newVal): (char *) newVal;
+     next_elt->key_checksum = checksum;
+ }
+ 
+ APR_DECLARE(void) apr_table_set_one(apr_table_t *t, const char *key, const char 
*oldVal,
+                        const char *newVal)
+ {
+       apr_table_set_one_internal(t, key, oldVal, newVal, 1);
+       return;
+ }
+ 
+ APR_DECLARE(void) apr_table_setn_one(apr_table_t* t, const char *key, const char 
*oldVal,
+                   const char *newVal)
+ {
+       apr_table_set_one_internal(t, key, oldVal, newVal, 0);
+       return;
+ }
+ 
+ APR_DECLARE(void) apr_table_unset_one(apr_table_t *t, const char *key, const char* 
val)
+ {
+     apr_table_entry_t *next_elt;
+     apr_table_entry_t *end_elt;
+     apr_table_entry_t *dst_elt;
+     apr_uint32_t checksum;
+     int hash;
+     int must_reindex;
+ 
+     hash = TABLE_HASH(key);
+     if (!TABLE_INDEX_IS_INITIALIZED(t, hash)) {
+         return;
+     }
+     COMPUTE_KEY_CHECKSUM(key, checksum);
+     next_elt = ((apr_table_entry_t *) t->a.elts) + t->index_first[hash];
+     end_elt = ((apr_table_entry_t *) t->a.elts) + t->index_last[hash];
+     must_reindex = 0;
+     for (; next_elt <= end_elt; next_elt++) {
+         if ((checksum == next_elt->key_checksum) &&
+             !strcasecmp(next_elt->key, key) &&
+             (!val || !strcmp(next_elt->val, val))) {
+             /* Found a match: remove this entry */
+             apr_table_entry_t *table_end = ((apr_table_entry_t *) t->a.elts) +
+                 t->a.nelts;
+             t->a.nelts--;
+             dst_elt = next_elt;
+ 
+             /* Shift over the remainder of the table.*/
+             for (next_elt++; next_elt < table_end; next_elt++) {
+                 *dst_elt++ = *next_elt;
+             }
+             must_reindex = 1;
+             break;
+         }
+     }
+     if (must_reindex) {
+         table_reindex(t);
+     }
+ }
+ 
*** apr_tables.h        Thu Apr 15 18:10:49 2004
--- apr_tables++.h      Thu Apr 15 18:10:26 2004
***************
*** 230,235 ****
--- 230,254 ----
                                  const char *val);
  
  /**
+  * Replace the value of  an element with a specific  key and value. If
+  * <oldVal> is NULL,  it will modify the value  of the first occurence
+  * of an element with the given key. If such an  element is not found,
+  * it will add a new element to  the table. Unlike apr_table_set which
+  * will remove  any multiple instances   of an element, this  function
+  * modifies or adds exactly one element in the table.
+  * @param t The table to add the data to.
+  * @param key The key of the element to modify.
+  * @param oldVal The value of the element to modify.
+  * @param newVal The new value of the element.
+  * @remark When adding data, this function makes a copy of both the key and the
+  *         value.
+  */
+ APR_DECLARE(void) apr_table_set_one(apr_table_t* t,
+                                                                       const char* 
key,
+                                                                       const char* 
oldVal,
+                                                                       const char* 
newVal);
+       
+ /**
   * Add a key/value pair to a table, if another element already exists with the
   * same key, this will over-write the old data.
   * @param t The table to add the data to.
***************
*** 243,248 ****
--- 262,288 ----
                                   const char *val);
  
  /**
+  * Replace the value of  an element with a specific  key and value. If
+  * <oldVal> is NULL,  it will modify the value  of the first occurence
+  * of an element with the given key. If such an  element is not found,
+  * it will add a new element to the table. Unlike apr_table_setn which
+  * will remove  any multiple instances  of the element, this  function
+  * modifies or adds exactly one element in the table.
+  * @param t The table to add the data to.
+  * @param key The key of the element to modify.
+  * @param oldVal The value of the element to modify.
+  * @param newVal The new value of the element.
+  * @remark This function is identical to apr_table_set_one except that
+  *               it does not make copies of key or newVal so care should be
+  *               taken to ensure that the values will not change after they have
+  *               been added.
+  */
+ APR_DECLARE(void) apr_table_setn_one(apr_table_t* t,
+                                                                        const char* 
key,
+                                                                        const char* 
oldVal,
+                                                                        const char* 
newVal);
+       
+ /**
   * Remove data from the table
   * @param t The table to remove data from
   * @param key The key of the data being removed
***************
*** 250,255 ****
--- 290,311 ----
  APR_DECLARE(void) apr_table_unset(apr_table_t *t, const char *key);
  
  /**
+  * Remove an element with the given key and value.  It does nothing if
+  * it can't find a header  with the given key  and value. If <val>  is
+  * NULL, it  will remove  the first  occurance of an  element with the
+  * given  key - this feature is   an optimization for situations where
+  * you know  that only  one element exists  and don't  care about  its
+  * current value.  Unlike  apr_table_unset (which removes all elements
+  * having  the give key)  this function  will  remove at the most  one
+  * element from the table.
+  * @param t The table to remove data from
+  * @param key The key of the data being removed
+  * @param val Value of the element to remove.
+  */
+ APR_DECLARE(void) apr_table_unset_one(apr_table_t* t,
+                                                                         const char* 
key,
+                                                                         const char* 
val);
+ /**
   * Add data to a table by merging the value with data that has already been 
   * stored
   * @param t The table to search for the data

Reply via email to