OpenCV源码解析:Hash链表的生成(cvGetFileNode)与读取(cvGetFileNodeByName)

从父节点派生子节点cvGetFileNode

从CvFileNode* _map_node中,根据key生成一个node,设置后返回这个生成的node。

举个应用的例子: 在函数icvXMLParseValue中,当读取XML分类器中一个map_node”<_>”时,其相应的key为key(rect),一个rect有4个参数(左上角x,y,右下角x,y),icvXMLParseValue通过以便代码, key->str.len == 4 && key->str.ptr[0] == 'rect'; elem = cvGetFileNode( fs, node, key, 1 ); 得到一个map_node的子节点elem,其elem->key = key(rect)。

当返回到icvXMLParseValue后,只需要继续填充elem节点的内容(左上角x,y,右下角x,y),即可完成该子节点的操作。

// persistance_c.cpp
CV_IMPL CvFileNode*
cvGetFileNode( CvFileStorage* fs, CvFileNode* _map_node,
               const CvStringHashNode* key,
               int create_missing )
{
    CvFileNode* value = 0;
    int k = 0, attempts = 1;
​
    if( !fs )
        return 0;
​
    CV_CHECK_FILE_STORAGE(fs);
​
    if( !key )
        CV_Error( CV_StsNullPtr, "Null key element" );
​
    if( _map_node )
    {
        if( !fs->roots )
            return 0;
        attempts = fs->roots->total;
    }
​
    for( k = 0; k < attempts; k++ )
    {
        int i, tab_size;
        CvFileNode* map_node = _map_node;
        CvFileMapNode* another;
        CvFileNodeHash* map;
​
        if( !map_node )
            map_node = (CvFileNode*)cvGetSeqElem( fs->roots, k );
        CV_Assert(map_node != NULL);
        if( !CV_NODE_IS_MAP(map_node->tag) )
        {
            if( (!CV_NODE_IS_SEQ(map_node->tag) || map_node->data.seq->total != 0) &&
                CV_NODE_TYPE(map_node->tag) != CV_NODE_NONE )
                CV_Error( CV_StsError, "The node is neither a map nor an empty collection" );
            return 0;
        }
​
        map = map_node->data.map;
        tab_size = map->tab_size;
​
        if( (tab_size & (tab_size - 1)) == 0 )
            i = (int)(key->hashval & (tab_size - 1));
        else
            i = (int)(key->hashval % tab_size);
​
        for( another = (CvFileMapNode*)(map->table[i]); another != 0; another = another->next )
            if( another->key == key )
            {
                if( !create_missing )
                {
                    value = &another->value;
                    return value;
                }
                CV_PARSE_ERROR( "Duplicated key" );
            }
​
        if( k == attempts - 1 && create_missing )
        {
            // 从map生成一个新的node(获取node空间)
            CvFileMapNode* node = (CvFileMapNode*)cvSetNew( (CvSet*)map ); 
            node->key = key;
​
            node->next = (CvFileMapNode*)(map->table[i]);
            map->table[i] = node;
            value = (CvFileNode*)node;
        }
    }
​
    return value;
}

读取节点信息cvGetFileNodeByName

从各节点中读取节点信息的函数是cvGetFileNodeByName,它采用Hash快速排序的方法,快速地从fs中读取CvFileNode。

CV_IMPL CvFileNode*
cvGetFileNodeByName( const CvFileStorage* fs, const CvFileNode* _map_node, const char* str )
{
    CvFileNode* value = 0;
    int i, len, tab_size;
    unsigned hashval = 0;
    int k = 0, attempts = 1;
​
    if( !fs )
        return 0;
​
    CV_CHECK_FILE_STORAGE(fs);
​
    if( !str )
        CV_Error( CV_StsNullPtr, "Null element name" );
​
    // 计算Hash值及表的序号i.
    for( i = 0; str[i] != '\0'; i++ )
        hashval = hashval*CV_HASHVAL_SCALE + (unsigned char)str[i];
    hashval &= INT_MAX;
    len = i; // 字符串长度
​
    if( !_map_node )
    {
        if( !fs->roots )
            return 0;
        attempts = fs->roots->total;
    }
​
    for( k = 0; k < attempts; k++ )
    {
        CvFileNodeHash* map;
        const CvFileNode* map_node = _map_node;
        CvFileMapNode* another;
​
        if( !map_node )
            map_node = (CvFileNode*)cvGetSeqElem( fs->roots, k );
​
        if( !CV_NODE_IS_MAP(map_node->tag) )
        {
            if( (!CV_NODE_IS_SEQ(map_node->tag) || map_node->data.seq->total != 0) &&
                CV_NODE_TYPE(map_node->tag) != CV_NODE_NONE )
                CV_Error( CV_StsError, "The node is neither a map nor an empty collection" );
            return 0;
        }
​
        map = map_node->data.map;
        tab_size = map->tab_size;
​
        // 从Hash值hashval得到表的序号i
        if( (tab_size & (tab_size - 1)) == 0 )
            i = (int)(hashval & (tab_size - 1));
        else
            i = (int)(hashval % tab_size);
​
        for( another = (CvFileMapNode*)(map->table[i]); another != 0; another = another->next )
        {
            const CvStringHashNode* key = another->key;
            
            // 如果Hash值,序号等都匹配的话,就返回读取的节点
            if( key->hashval == hashval &&
                key->str.len == len &&
                memcmp( key->str.ptr, str, len ) == 0 )
            {
                value = &another->value;
                return value;
            }
        }
    }
    return value;
}

常用节点的结构定义

为了方便,我这里列出常用节点的结构定义,

// ref. types_c.h
typedef struct CvString
{
    int len;
    char* ptr;
}
CvString;
​
/** All the keys (names) of elements in the readed file storage
   are stored in the hash to speed up the lookup operations: */
typedef struct CvStringHashNode
{
    unsigned hashval;
    CvString str;
    struct CvStringHashNode* next;
}
CvStringHashNode;
​
/** Basic element of the file storage - scalar or collection: */
typedef struct CvFileNode
{
    int tag;
    struct CvTypeInfo* info; /**< type information
            (only for user-defined object, for others it is 0) */
    union
    {
        double f; /**< scalar floating-point number */
        int i;    /**< scalar integer number */
        CvString str; /**< text string */
        CvSeq* seq; /**< sequence (ordered collection of file nodes) */
        CvFileNodeHash* map; /**< map (collection of named file nodes) */
    } data;
}
CvFileNode;
​
// ref. persistance.cpp
typedef struct CvFileMapNode
{
    CvFileNode value;
    const CvStringHashNode* key;
    struct CvFileMapNode* next;
}
CvFileMapNode;
​
// 一些常的Hash定义
typedef struct CvGenericHash
{
    CV_SET_FIELDS()
    int tab_size;
    void** table;
}
CvGenericHash;
typedef CvGenericHash CvStringHash;
typedef struct CvGenericHash CvFileNodeHash;

 

 

posted @ 2018-08-23 21:27  SpaceVision  阅读(39)  评论(0编辑  收藏  举报