/*
 * name-hash.c
 *
 * Hashing names in the index state
 *
 * Copyright (C) 2008 Linus Torvalds
 */
#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "index.h"

/*
 * This removes bit 5 if bit 6 is set.
 *
 * That will make US-ASCII characters hash to their upper-case
 * equivalent. We could easily do this one whole word at a time,
 * but that's for future worries.
 */
static inline unsigned char icase_hash(unsigned char c)
{
    return c & ~((c & 0x40) >> 1);
}

unsigned int hash_name(const char *name, int namelen)
{
    unsigned int hash = 0x123;

    do {
        unsigned char c = *name++;
        c = icase_hash(c);
        hash = hash*101 + c;
    } while (--namelen);
    return hash;
}

#if 0
static void hash_index_entry_directories(struct index_state *istate, struct cache_entry *ce)
{
    /*
     * Throw each directory component in the hash for quick lookup
     * during a git status. Directory components are stored with their
     * closing slash.  Despite submodules being a directory, they never
     * reach this point, because they are stored without a closing slash
     * in the cache.
     *
     * Note that the cache_entry stored with the directory does not
     * represent the directory itself.  It is a pointer to an existing
     * filename, and its only purpose is to represent existence of the
     * directory in the cache.  It is very possible multiple directory
     * hash entries may point to the same cache_entry.
     */
    unsigned int hash;
    void **pos;

    const char *ptr = ce->name;
    while (*ptr) {
        while (*ptr && *ptr != '/')
            ++ptr;
        if (*ptr == '/') {
            ++ptr;
            hash = hash_name(ce->name, ptr - ce->name);
            if (!lookup_hash(hash, &istate->name_hash)) {
                pos = insert_hash(hash, ce, &istate->name_hash);
                if (pos) {
                    ce->next = *pos;
                    *pos = ce;
                }
            }
        }
    }
}
#endif

static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
{
    void **pos;
    unsigned int hash;

    if (ce->ce_flags & CE_HASHED)
        return;
    ce->ce_flags |= CE_HASHED;
    ce->next = NULL;
    hash = hash_name(ce->name, ce_namelen(ce));
    pos = insert_hash(hash, ce, &istate->name_hash);
    if (pos) {
        ce->next = *pos;
        *pos = ce;
    }

    /* if (ignore_case) */
    /*  hash_index_entry_directories(istate, ce); */
}

static void lazy_init_name_hash(struct index_state *istate)
{
    int nr;

    if (istate->name_hash_initialized)
        return;
    for (nr = 0; nr < istate->cache_nr; nr++)
        hash_index_entry(istate, istate->cache[nr]);
    istate->name_hash_initialized = 1;
}

void add_name_hash(struct index_state *istate, struct cache_entry *ce)
{
    ce->ce_flags &= ~CE_UNHASHED;
    if (istate->name_hash_initialized)
        hash_index_entry(istate, ce);
}

#if 0
static int slow_same_name(const char *name1, int len1, const char *name2, int len2)
{
    if (len1 != len2)
        return 0;

    while (len1) {
        unsigned char c1 = *name1++;
        unsigned char c2 = *name2++;
        len1--;
        if (c1 != c2) {
            c1 = toupper(c1);
            c2 = toupper(c2);
            if (c1 != c2)
                return 0;
        }
    }
    return 1;
}
#endif

static int same_name(const struct cache_entry *ce, const char *name, int namelen, int icase)
{
    int len = ce_namelen(ce);

    /*
     * Always do exact compare, even if we want a case-ignoring comparison;
     * we do the quick exact one first, because it will be the common case.
     */
    if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
        return 1;
    else
        return 0;

    /* if (!icase) */
    /*  return 0; */

    /*
     * If the entry we're comparing is a filename (no trailing slash), then compare
     * the lengths exactly.
     */
    /* if (name[namelen - 1] != '/') */
    /*  return slow_same_name(name, namelen, ce->name, len); */

    /*
     * For a directory, we point to an arbitrary cache_entry filename.  Just
     * make sure the directory portion matches.
     */
    /* return slow_same_name(name, namelen, ce->name, namelen < len ? namelen : len); */
}

struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
{
    unsigned int hash = hash_name(name, namelen);
    struct cache_entry *ce;

    lazy_init_name_hash(istate);
    ce = lookup_hash(hash, &istate->name_hash);

    while (ce) {
        if (!(ce->ce_flags & CE_UNHASHED)) {
            if (same_name(ce, name, namelen, icase))
                return ce;
        }
        ce = ce->next;
    }

    /*
     * Might be a submodule.  Despite submodules being directories,
     * they are stored in the name hash without a closing slash.
     * When ignore_case is 1, directories are stored in the name hash
     * with their closing slash.
     *
     * The side effect of this storage technique is we have need to
     * remove the slash from name and perform the lookup again without
     * the slash.  If a match is made, S_ISGITLINK(ce->mode) will be
     * true.
     */
    /* if (icase && name[namelen - 1] == '/') { */
    /*  ce = index_name_exists(istate, name, namelen - 1, icase); */
    /*  if (ce && S_ISGITLINK(ce->ce_mode)) */
    /*      return ce; */
    /* } */
    return NULL;
}
