Hi,
Thanks for the review, Eric. I have replied to your comments below,
I will try to reply early and more promptly now.
On 06/10/2014 04:27 AM, Eric Sunshine wrote:
>> ---
>> diff --git a/Documentation/technical/api-config.txt
>> b/Documentation/technical/api-config.txt
>> index 230b3a0..5b6e376 100644
>> --- a/Documentation/technical/api-config.txt
>> +++ b/Documentation/technical/api-config.txt
>> @@ -77,6 +77,24 @@ To read a specific file in git-config format, use
>> `git_config_from_file`. This takes the same callback and data parameters
>> as `git_config`.
>>
>> +Querying For Specific Variables
>> +-------------------------------
>> +
>> +For programs wanting to query for specific variables in a non-callback
>> +manner, the config API provides two functions `git_config_get_string`
>> +and `git_config_get_string_multi`. They both take a single parameter,
>> +
>> +- a key string in canonical flat form for which the corresponding values
>> + will be retrieved and returned.
>
> It's strange to have a bulleted list for a single item. It can be
> stated more naturally as a full sentence, without the list.
Point Noted.
>> +They both read values from an internal cache generated previously from
>> +reading the config files. `git_config_get_string` returns the value with
>> +the highest priority(i.e. for the same variable value in the repo config
>> +will be preferred over value in user wide config).
>> +
>> +`git_config_get_string_multi` returns a `string_list` containing all the
>> +values for that particular key, sorted in order of increasing priority.
>> +
>> Value Parsing Helpers
>> ---------------------
>>
>> diff --git a/config.c b/config.c
>> index a30cb5c..6f6b04e 100644
>> --- a/config.c
>> +++ b/config.c
>> @@ -9,6 +9,8 @@
>> #include "exec_cmd.h"
>> #include "strbuf.h"
>> #include "quote.h"
>> +#include "hashmap.h"
>> +#include "string-list.h"
>>
>> struct config_source {
>> struct config_source *prev;
>> @@ -37,6 +39,120 @@ static struct config_source *cf;
>>
>> static int zlib_compression_seen;
>>
>> +struct config_cache_entry {
>> + struct hashmap_entry ent;
>> + char *key;
>> + struct string_list *value_list;
>
> Same question as in my v1 and v2 reviews [1][2], and reiterated by
> Matthieu [3]: Why is 'value_list' a pointer to a structure rather than
> just a structure?
>
Sorry for the lack of response on this part. I thought choosing a pointer to a
structure or just the structure itself was a stylistic choice. Since most of the
functions just pass the pointer to this struct, I thought it would be more
natural to
use it. But I have changed my mind on this one. In the next iteration I will be
using
a plane old struct.
>
>> +};
>> +
>> +static int hashmap_is_init;
>> +
>> +static int config_cache_set_value(const char *key, const char *value);
>> +
>> +static int config_cache_entry_cmp_case(const struct config_cache_entry *e1,
>> + const struct config_cache_entry *e2, const
>> void *unused)
>> +{
>> + return strcmp(e1->key, e2->key);
>
> As suggested by Peff [4], this comparison is now case-sensitive,
> instead of case-insensitive as in the previous version. Rather than
> changing the appended '_icase' to '_case', a more typical function
> name would be simply config_cache_entry_cmp().
Noted.
>> +}
>> +
>> +static void config_cache_init(struct hashmap *config_cache)
>> +{
>> + hashmap_init(config_cache, (hashmap_cmp_fn)
>> config_cache_entry_cmp_case, 0);
>
> In his review [4], Peff suggested giving config_cache_entry_cmp_case()
> the correct hashmap_cmp_fn signature so that this cast can be dropped.
> It's not clear whether you disagree with his advice or overlooked or
> forgot about it. If you disagree with his suggestion, it's okay to say
> so and explain why you feel the way you do, but without feedback from
> you, he or another reviewer is going to continue suggesting the same
> change.
Now on this one, I checked the code thoroughly. Every single hashmap_init()
incantation in git code has a hashmap_cmp_fn cast. I don't think that it is
necessary to do so. Is it?
>> +}
>> +
>> +static int config_cache_callback(const char *key, const char *value, void
>> *unused)
>> +{
>> + config_cache_set_value(key, value);
>> + return 0;
>> +}
>> +
>> +static struct hashmap *get_config_cache(void)
>> +{
>> + static struct hashmap config_cache;
>> + if (!hashmap_is_init) {
>> + config_cache_init(&config_cache);
>> + hashmap_is_init = 1;
>> + git_config(config_cache_callback, NULL);
>> + return &config_cache;
>
> Why do you 'return' here rather than just falling through to the
> 'return' outside the conditional?
Noted.
>> +static struct config_cache_entry *config_cache_find_entry(const char *key)
>> +{
>> + struct hashmap *config_cache;
>> + struct config_cache_entry k;
>> + char *normalized_key;
>> + int baselength = 0, ret;
>> + config_cache = get_config_cache();
>> + ret = git_config_parse_key(key, &normalized_key, &baselength);
>
> Since you neither care about nor ever reference 'baselength', you
> should just pass NULL as the third argument.
>
Noted.
>> + if (ret)
>> + return NULL;
>> +
>> + hashmap_entry_init(&k, strhash(normalized_key));
>> + k.key = (char*) key;
>
> This looks fishy. You're hashing based upon 'normalized_key' but then
> comparing against the unnormalized 'key'. Peff's suggestion [4] was to
> store the normalized key in the hash, which means that you should use
> 'normalized_key' for both hashing and comparing. (See additional
> commentary about this issue below in config_cache_set_value().)
Ouch, this I had corrected in a future commit. But forgot to include in
this patch.
> Style: (char *)key
Noted. In function arguments the code uses (char*) key. I copied it from there.
:)
>> + return hashmap_get(config_cache, &k, NULL);
>
> You're leaking 'normalized_key', which git_config_parse_key()
> allocated on your behalf.
>
Noted. I will now check with valgrind before sending any future series.
>> +}
>> +
>> +static struct string_list *config_cache_get_value(const char *key)
>> +{
>> + struct config_cache_entry *e = config_cache_find_entry(key);
>> + return e ? e->value_list : NULL;
>> +}
>> +
>> +
>> +static int config_cache_set_value(const char *key, const char *value)
>> +{
>> + struct hashmap *config_cache;
>> + struct config_cache_entry *e;
>> +
>> + config_cache = get_config_cache();
>> + e = config_cache_find_entry(key);
>> + if (!e) {
>> + e = xmalloc(sizeof(*e));
>> + hashmap_entry_init(e, strhash(key));
>
> The hash computed to store the key should be the same as the hash to
> look it up. In config_cache_find_entry(), you're correctly computing
> the hash based upon the normalized key, but here you're doing so from
> the unnormalized key.
>
>> + e->key = xstrdup(key);
>
> Likewise. Normalized keys should be stored in the hash, not unnormalized.
>
> You should be invoking git_config_parse_key() to normalize the key
> both for hashing and storing.
>
> Note, also, that call git_config_parse_key() allocates the normalize
> key on your behalf, so you do *not* want to xstrdup() it here.
config_cache_set_value() gets its values from git_config() as it the callback.
git_config() feeds the callback only normalized values by using functions like
get_extended_base_var(), get_base_var() etc. Thus, I didn't use
git_config_parse_key(). Please correct me if I am wrong, but I tested this case
thoroughly.
>> + e->value_list = xcalloc(sizeof(struct string_list), 1);
>> + e->value_list->strdup_strings = 1;
>
> This code is perhaps too intimate with the implementation of
> string_list. It may work today (because it is safe to initialize all
> string_list fields to 0 via xcalloc()), but a future change to the
> string_list implementation may invalidate that assumption. The
> initialization macros in string-list.h exist to preserve the
> abstraction, so you don't have to know the details of initialization.
> The macros are not suitable for your use-case, but an initialization
> function, such as string_list_init(), would be suitable, and it would
> be appropriate to add such a function in a preparatory patch (as
> already suggested by [1]).
Noted. As I am going to use a simple struct for the list, this won't be
a problem.
>> + string_list_append(e->value_list, value);
>> + hashmap_add(config_cache, e);
>> + } else {
>> + string_list_append(e->value_list, value);
>> + }
>> + return 0;
>> +}
>> +
>> +extern const char *git_config_get_string(const char *key)
>
> Drop the 'extern'. The header already declares it such.
>
Noted.
>> +{
>> + struct string_list *values;
>> + values = config_cache_get_value(key);
>> + if (!values)
>> + return NULL;
>> + assert(values->nr > 0);
>> + return values->items[values->nr - 1].string;
>> +}
>> +
>> +extern const struct string_list *git_config_get_string_multi(const char
>> *key)
>
> Drop the 'extern'. The header already declares it such.
>
Noted.
>> +{
>> + return config_cache_get_value(key);
>> +}
>> +
>> static int config_file_fgetc(struct config_source *conf)
>> {
>> return fgetc(conf->u.file);
>> @@ -1700,6 +1816,12 @@ int git_config_set_multivar_in_file(const char
>> *config_filename,
>> lock = NULL;
>> ret = 0;
>>
>> + /* contents of config file has changed, so invalidate the
>> + * config cache used by non-callback based query functions.
>> + */
>> + if (hashmap_is_init)
>> + config_cache_free();
>> +
>> out_free:
>> if (lock)
>> rollback_lock_file(lock);
>> --
>> 1.9.0.GIT
>
> [1]:
> http://git.661346.n2.nabble.com/RFC-PATCH-0-2-Git-config-cache-amp-special-querying-api-utilizing-the-cache-td7611691.html#a7611860
> [2]:
> http://thread.gmane.org/gmane.comp.version-control.git/250566/focus=251058
> [3]:
> http://thread.gmane.org/gmane.comp.version-control.git/251073/focus=251079
> [4]:
> http://thread.gmane.org/gmane.comp.version-control.git/250566/focus=250618
>
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html