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;