这篇文章中的代码是在前一篇文章中的代码的基础上进行改进而来的,觉得在前一篇文章中的
TorrentFile::encode 方法中关于 announce-list 列表类型和 info 字典类型的处理代码语句,
对于一个模块来说所占行实在是太繁琐了,所以将其中的 announce-list 和 info 的处理代码单独提取出来,
进行重新的封装。
分别在 torrentParser.h 头文件中添加进入了一些额外的方法,以及在 torrentParser.cpp 文件中给出了这些方法的实现,
于是就有了版本 2 , 其余的文件没有太大的变动。
修改之后的流程大概是这样的:
在 TorrentFile::encode 方法中,处理完字段 announce(string type) , comment( string type) , created by(string type)
creation date( integer type ) encoding ( string type ) 之后,将会调用 get_announce_list 方法来单独处理 announce-list ( list type )字段 ;
随后处理 info (dict type) 字段的时候将会调用方法 get_info_dict , 在该方法中将会把对 info 字段的处理分为 2 个部分
1. 公共部分
2. 单文件/多文件 模式
从公共部分中通过对 info 中的关键字 "files" 的查找,来判断 info 中的文件模式, 如果找到(即指向查找方法的指针不为空), 则是多文件模式
接下来调用 get_info_multi_fmode 方法来进行后续的处理。
如果找不到,即调用查找方法的时候,返回的指针为空 ,这种情况说明了 info 对应的是单文件格式,
接下来调用 get_info_single_mode 方法继续后续处理
// torrentParser.h
-
#ifndef TORRENT_PARSER_H
-
#define TORRENT_PARSER_H
-
-
#include <string>
-
#include <stdint.h> // int64_t
-
#include <map>
-
#include <vector>
-
#include <iostream>
-
-
using namespace std ;
-
-
typedef struct _file
-
{
-
int64_t file_length ;
-
string file_path ;
-
} file_t;
-
-
-
typedef struct _info
-
{
-
int64_t piece_length ; // length of each file block (B)
-
string pieces ; // hash value length of 20*n
-
-
bool is_multi_file ;
-
vector<file_t> file_list ;
-
} info_t ;
-
-
typedef struct _torrent_file
-
{
-
string announce ;
-
vector<string> announce_list ;
-
string comment ;
-
string created_by ;
-
int64_t creation_date ;
-
string encoding ;
-
info_t info ;
-
} torrent_file_t ;
-
-
-
class AnyNode
-
{
-
public :
-
virtual ~AnyNode () {}
-
virtual void print () = 0 ;
-
virtual bool parser (string &content) = 0 ;
-
} ;
-
-
class IntegerNode : public AnyNode
-
{
-
public :
-
virtual ~IntegerNode () {}
-
virtual bool parser( std::string &content ) ;
-
-
virtual void print ()
-
{
-
cout<<"integer type , value ["<< _value <<"]" <<endl;
-
}
-
-
int64_t _value ;
-
} ;
-
-
class StringNode : public AnyNode
-
{
-
public :
-
virtual ~StringNode () {}
-
virtual bool parser ( string &content ) ;
-
-
virtual void print ()
-
{
-
cout<<"string type ,value ["<< _value <<"]"<<endl;
-
}
-
-
string _value ;
-
} ;
-
-
class ListNode : public AnyNode
-
{
-
public :
-
virtual ~ListNode ()
-
{
-
for ( vector<AnyNode*>::iterator it = _value_list.begin () ;
-
it != _value_list.end () ; it++ )
-
{
-
delete *it ; // equal delete AnyNode *
-
}
-
-
_value_list.clear () ;
-
}
-
-
virtual bool parser ( string &content ) ;
-
virtual void print ()
-
{
-
cout << "type list "<< endl ;
-
for ( vector<AnyNode*>::iterator it = _value_list.begin () ;
-
it != _value_list.end () ; it++ )
-
{
-
AnyNode* it1 = *it ;
-
-
cout << "value " ;
-
it1->print () ;
-
cout << endl ;
-
}
-
}
-
-
vector<AnyNode*> _value_list ;
-
} ;
-
-
class DictNode : public AnyNode
-
{
-
public :
-
~DictNode ()
-
{
-
for ( map<StringNode *, AnyNode*>::iterator it = _value_map.begin () ;
-
it != _value_map.end () ; it++ )
-
{
-
delete it->first ;
-
delete it->second ;
-
}
-
-
_value_map.clear () ;
-
}
-
-
virtual bool parser ( string &content ) ;
-
-
virtual void print ()
-
{
-
StringNode *it1 ;
-
AnyNode *it2 ;
-
-
cout << "type dictionary "<< endl ;
-
-
for ( map<StringNode *, AnyNode *>::iterator it = _value_map.begin () ;
-
it != _value_map.end () ; it++ )
-
{
-
it1 = it->first ;
-
it2 = it->second ;
-
-
cout << "key " << endl ;
-
it1->print();
-
cout << "value " << endl ;
-
it2->print () ;
-
}
-
}
-
-
map<StringNode*, AnyNode*> _value_map ;
-
} ;
-
-
class TorrentFile
-
{
-
public :
-
torrent_file_t torrent_file ;
-
-
static bool encode ( const string & torrent_file_content ,
-
torrent_file_t &torrent_structure ) ;
-
-
// update date : 2015/4/15 by Aimer
-
-
static void get_node_value ( IntegerNode *pIntegerNode , int64_t &integer_value ) ;
-
-
static void get_node_value ( StringNode *pStringNode , string &string_value ) ;
-
-
static AnyNode *find_target_node ( map<StringNode *, AnyNode*> &hash_map , const string &key ) ;
-
-
static void get_announce_list ( ListNode*pAnnounceList , torrent_file_t &torrent_structure ) ;
-
-
static void get_info_dict ( DictNode*pInfoDict , torrent_file_t &torrent_structure ) ;
-
-
// this method will be called by get_info_dict
-
static void get_info_common( DictNode*pInfoDict , torrent_file_t &torrent_structure ) ;
-
-
// this method will be called by get_info_dict when single-file mode
-
static void get_info_single_mode ( DictNode* pInfoDict ,torrent_file_t &torrent_structure) ;
-
-
// this method will be called by get_info_dict when multi-file mode
-
static void get_info_multi_mode ( DictNode* pInfoDict , torrent_file_t &torrent_structure ) ;
-
-
-
// this method will print out the messages stored inside in the torrent_file_t
-
static void show_torrent_content ( torrent_file_t &torrent_structure) ;
-
-
} ;
-
-
#endif
// torrentParser.cpp
-
#include <cstdio>
-
#include <cstring>
-
#include <iostream>
-
-
#include <vector>
-
#include <map>
-
-
#include <glog/logging.h>
-
-
#include "parserUtil.h"
-
#include "torrentParser.h"
-
-
using namespace std ;
-
-
bool IntegerNode::parser ( string &content )
-
{
-
if ( content.empty () )
-
{
-
LOG(WARNING)<< "[warnning] in integer's parser content is empty";
-
return false ;
-
}
-
-
if ( content[0] != 'i' )
-
{
-
LOG(WARNING)<< "[warnning] integer's parser content first element illegal";
-
return false ;
-
}
-
-
uint64_t pos = content.find ( 'e' , 0 ) ;
-
// find the position of char e from beginning of content
-
-
if ( pos == string::npos )
-
{
-
LOG(WARNING) << "[warnning] integer's parser content not find char e";
-
return false ;
-
}
-
-
string s_value = content.substr(1 , pos-1) ;
-
// extract the sub-string integer from content with out begin 'i' and end 'e'
-
-
parserUtils::string_to_integer (s_value , _value ) ;
-
// transfer string into integer(int64_t)
-
-
content = content.erase ( 0 , pos+1 ) ;
-
-
LOG(INFO) << "[info] integer's parser extract message "<< _value << " remain message " << content ;
-
return true ;
-
}
-
-
bool StringNode::parser ( string & content )
-
{
-
if ( content.empty () )
-
{
-
LOG(WARNING) <<"[warnning] string type's parser content is empty ";
-
return false ;
-
}
-
-
if ( content.size () < 3 )
-
{
-
LOG(WARNING)<<"[warnning] string type's parser content length is illegal" ;
-
return false ;
-
}
-
-
int64_t pos = content.find (':', 0) ;
-
// find ':' in string_length:string_content structure
-
-
if ( pos == string::npos )
-
{
-
LOG(WARNING)<<"[warnning] string type's it is illegal string type with out ':' ";
-
return false ;
-
}
-
-
int64_t count = 0 ;
-
-
parserUtils::string_to_integer (content.substr(0 , pos) , count ) ;
-
// extract string 's length from content
-
-
_value = content.substr (pos+1, count ) ;
-
cout << " in method string node parser " << _value << endl ;
-
cout << " length " << count << endl ;
-
// pos+1 : beginning of the string content , count : length of the string content
-
-
content = content.erase (0 , pos+count+1) ;
-
// pos : number of the length , count : numbers of the string's char
-
// eraser method erase range [begin , end) 1 should be plus to (pos+count)
-
-
LOG(INFO)<<"[info] string type parser extract message "<< _value
-
<< " remain content " << content ;
-
return true ;
-
}
-
-
-
bool ListNode::parser ( string &content )
-
{
-
if ( content.empty () )
-
{
-
LOG(WARNING)<<"[warnning] list type parser content is empty ";
-
return false ;
-
}
-
-
if ( content[0] != 'l' )
-
{
-
LOG(WARNING)<<"[warnning] list type parser content with out list mark" ;
-
return false ;
-
}
-
-
content = content.erase ( 0 , 1 ) ;
-
// delete 'l' from content
-
-
while ( !content.empty () )
-
{
-
AnyNode *anyNode = NULL ;
-
-
if ( content [0] == 'l' )
-
{
-
anyNode = new ListNode () ;
-
}
-
-
else if ( content [0] == 'd')
-
{
-
anyNode = new DictNode () ;
-
}
-
-
else if ( content [0] == 'i' )
-
{
-
anyNode = new IntegerNode () ;
-
}
-
-
else if (content[0] >= '1' && content[0] <= '9')
-
{
-
anyNode = new StringNode () ;
-
}
-
-
else
-
{
-
LOG(WARNING)<<"[warnning] list parser list contain illegal message" ;
-
return false;
-
}
-
-
anyNode->parser ( content ) ;
-
// this method will parse the content into the right value type
-
// and store it into the memeber value variable (_value , _value_list ,value_map...)
-
-
_value_list.push_back ( anyNode ) ;
-
-
if ( content[0] == 'e' ) // is it the end of the list type ?
-
{
-
// here we arrived the end of the list ,
-
// we should erase the 'e' in content and break the cycle
-
-
content = content.erase ( 0 , 1 ) ;
-
break ;
-
}
-
-
}// while
-
-
LOG(INFO)<<"[info] list type parser remainning content " << content ;
-
return true ;
-
}
-
-
bool DictNode::parser ( string & content )
-
{
-
if ( content[0] != 'd' )
-
{
-
LOG(WARNING)<<"dictionary type parser content not begin with 'd' " ;
-
return false ;
-
}
-
-
if ( content.size () < 3 )
-
{
-
LOG(WARNING)<<"dictionary type parser content length illegal " ;
-
return false ;
-
}
-
-
content = content.erase ( 0 , 1 ) ;
-
// erase 'd' from content
-
-
// dict type : <key: string type><value: any type>
-
-
while ( !content.empty () )
-
{
-
StringNode *key = new StringNode () ;
-
key->parser ( content ) ;
-
// extract key message from content
-
-
if ( content.empty () )
-
{
-
LOG(INFO) << "[info] content is empty ,break ";
-
break ;
-
}
-
-
AnyNode *value = NULL ;
-
-
if ( content [0] == 'i' )
-
value = new IntegerNode () ;
-
else if ( content [0] == 'l' )
-
value = new ListNode () ;
-
else if ( content [0] == 'd' )
-
value = new DictNode () ;
-
else if ( content[0] >= '1' && content[0] <= '9' )
-
value = new StringNode () ;
-
-
else
-
{
-
LOG(WARNING)<<"[warnning] dict type parser dict contains illegal type" ;
-
return false ;
-
}
-
-
value->parser ( content ) ;
-
_value_map[key] = value ;
-
-
if ( content[0] == 'e' )
-
{
-
// arrived the end of the dict ; erase the 'e' in content
-
content = content.erase( 0 , 1 );
-
break ;
-
}
-
}
-
-
return true ;
-
}
-
-
// update date : 2015/4/15 by Aimer
-
void TorrentFile::get_node_value (StringNode *pStringNode, string &string_value)
-
{
-
if ( pStringNode == NULL )
-
{
-
LOG(WARNING)<<"[warnning] can not get string type value , empty" ;
-
string_value = "" ;
-
}
-
else
-
{
-
string_value = pStringNode->_value ;
-
}
-
}
-
-
void TorrentFile::get_node_value ( IntegerNode *pIntegerNode , int64_t &integer_value )
-
{
-
if ( pIntegerNode == NULL )
-
{
-
LOG(WARNING)<<"[warnning] can not get integer type value , empty";
-
integer_value = -1 ;
-
}
-
else
-
{
-
integer_value = pIntegerNode->_value ;
-
}
-
}
-
-
AnyNode * TorrentFile::find_target_node ( map<StringNode*, AnyNode*>&hash_map ,
-
const string &key )
-
{
-
for ( map<StringNode*,AnyNode*>::iterator it_map = hash_map.begin() ;
-
it_map != hash_map.end() ; it_map++ )
-
{
-
StringNode *pStringNode = dynamic_cast<StringNode*>(it_map->first) ;
-
if ( pStringNode == NULL )
-
{
-
LOG(WARNING)<<"[warnning] hash map key is NULL ";
-
return NULL ;
-
}
-
-
if ( pStringNode->_value == key )
-
return it_map->second ;
-
}
-
-
return NULL ; // not find
-
}
-
-
/**
-
method : get_announce_list ( ListNode &announce_list , torrent_file_t &torrent_structure)
-
-
descri : this method is used to extract ListNode.StringNode (in b-encoding)
-
and transfer it into the std::string
-
then push it into the torrent_list.announce_list(vector<string>)
-
*/
-
-
void TorrentFile::get_announce_list ( ListNode *announce_list , torrent_file_t &torrent_structure)
-
{
-
string announce_string ;
-
-
for ( vector<AnyNode*>::iterator it_string = announce_list->_value_list.begin () ;
-
it_string != announce_list->_value_list.end () ; it_string++ )
-
{
-
get_node_value ( dynamic_cast<StringNode*>(*it_string) , announce_string ) ;
-
torrent_structure.announce_list.push_back ( announce_string ) ;
-
}
-
}
-
-
-
/*
-
this method is used as the main method to extract info dict message
-
call context :
-
AnyNode *pAnyNode_info_dict = find_target_node ( pFileDict->_value_map , "info" ) ;
-
if ( pAnyNode_info_dict != NULL )
-
{
-
DictNode *pInfoDict = dynamic_cast<DictNode*>(pAnyNode_info_dict) ;
-
get_info_dict( pInfoDict, torrent_structure ) ;
-
}
-
-
*/
-
void TorrentFile::get_info_dict ( DictNode *pInfoDict, torrent_file_t &torrent_structure )
-
{
-
get_info_common ( pInfoDict , torrent_structure ) ;
-
-
if ( torrent_structure.info.is_multi_file )
-
{
-
// multi-file mode
-
get_info_multi_mode ( pInfoDict, torrent_structure ) ;
-
}
-
else
-
{
-
// single file mode
-
get_info_single_mode ( pInfoDict, torrent_structure) ;
-
}
-
-
}
-
-
// this method will be called by get_info_dict
-
void TorrentFile::get_info_common( DictNode *pInfoDict , torrent_file_t &torrent_structure )
-
{
-
// find info.pieces from info-dict , type : string type (b-encoding)
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node( pInfoDict->_value_map , "pieces" )) , torrent_structure.info.pieces ) ;
-
-
// find info.piece_length from info-dict , type : integer type (b-encoding)
-
get_node_value ( dynamic_cast<IntegerNode*>
-
(find_target_node( pInfoDict->_value_map, "piece length")),torrent_structure.info.piece_length) ;
-
-
AnyNode *pAnyNode_files_list = find_target_node ( pInfoDict->_value_map , "files" ) ;
-
-
if ( pAnyNode_files_list != NULL )
-
{
-
torrent_structure.info.is_multi_file = true ;
-
}
-
-
else
-
{
-
// single-file
-
torrent_structure.info.is_multi_file = false ;
-
}
-
}
-
-
// this method will be called by get_info_dict when single-file mode
-
void TorrentFile::get_info_single_mode ( DictNode*pInfoDict ,torrent_file_t &torrent_structure)
-
{
-
// this is single-mode file
-
file_t single_file ;
-
-
// get file name : string type
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node ( pInfoDict->_value_map , "name" )) , single_file.file_path) ;
-
-
get_node_value ( dynamic_cast<IntegerNode*>
-
(find_target_node ( pInfoDict->_value_map, "length")) , single_file.file_length ) ;
-
-
torrent_structure.info.file_list.push_back ( single_file ) ;
-
-
}
-
-
-
// this method will be called by get_info_dict when multi-file mode
-
void TorrentFile::get_info_multi_mode ( DictNode*pInfoDict,torrent_file_t &torrent_structure )
-
{
-
string main_dir ;
-
-
// multi-file , info.name is the main path name
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node(pInfoDict->_value_map , "name")) , main_dir) ;
-
-
// now the main_dir is the name of the main path of all the files
-
-
ListNode *pInfoFiles = dynamic_cast<ListNode*>( find_target_node ( pInfoDict->_value_map , "files" )) ;
-
-
// traverse the files list , each element in list are in type of dict
-
for ( vector<AnyNode*>::iterator it_sub_file = pInfoFiles->_value_list.begin () ;
-
it_sub_file != pInfoFiles->_value_list.end () ; it_sub_file++ )
-
{
-
// each element in the list are dict
-
DictNode *pInfoFilesSubFile = dynamic_cast<DictNode*>(*it_sub_file) ;
-
-
if ( pInfoFilesSubFile != NULL )
-
{
-
file_t sub_file ;
-
-
// get length of each sub file from the dict
-
get_node_value ( dynamic_cast<IntegerNode*>
-
( find_target_node ( pInfoFilesSubFile->_value_map, "length" ) ) , sub_file.file_length ) ;
-
-
AnyNode *pAnyNode_sub_path = find_target_node ( pInfoFilesSubFile->_value_map ,"path" ) ;
-
if ( pAnyNode_sub_path != NULL )
-
{
-
ListNode *pSubPathList = dynamic_cast<ListNode*>(pAnyNode_sub_path) ;
-
-
// traverse the list and extract each sub path and append them together
-
for ( vector<AnyNode*>::iterator it_sub_path = pSubPathList->_value_list.begin() ;
-
it_sub_path != pSubPathList->_value_list.end () ; it_sub_path++ )
-
{
-
string sub_path ;
-
-
sub_file.file_path += "//" ;
-
-
get_node_value ( dynamic_cast<StringNode*>(*it_sub_path) , sub_path ) ;
-
-
sub_file.file_path += sub_path ;
-
}
-
-
}
-
else
-
{
-
LOG(INFO)<<"[info] sub path list is empty";
-
}
-
-
torrent_structure.info.file_list.push_back (sub_file) ;
-
-
}
-
else
-
{
-
LOG(INFO)<<"[info] file dict is empty " ;
-
}
-
}// for
-
}
-
-
-
// following method is used the parser torrent file which is the combination
-
// of all the B encoding parsers (type: integer , string , list, dict ...)
-
-
bool TorrentFile::encode ( const string & torrent_file_content , torrent_file_t &torrent_structure )
-
{
-
string file_string = torrent_file_content ;
-
DictNode *pFileDict = new DictNode () ;
-
-
pFileDict->parser ( file_string ) ;
-
-
// key word : "announce" ; type: string
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node( pFileDict->_value_map , "announce" )) , torrent_structure.announce ) ;
-
-
// key word : "created by" ; type : string
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node(pFileDict->_value_map, "created by")) , torrent_structure.created_by) ;
-
-
// key word : "creatation date" ; type :integer
-
get_node_value ( dynamic_cast<IntegerNode*>
-
( find_target_node( pFileDict->_value_map , "creation date")) , torrent_structure.creation_date ) ;
-
-
// key word : "comment" ; type : string
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node( pFileDict->_value_map , "comment" ) ), torrent_structure.comment ) ;
-
-
// key word : "encoding" ; type : string
-
get_node_value ( dynamic_cast<StringNode*>
-
(find_target_node (pFileDict->_value_map , "encoding" )), torrent_structure.encoding ) ;
-
-
-
// key word : "announce-list" ; type : list< contain element type : string >
-
AnyNode *pAnyNode_list = find_target_node ( pFileDict->_value_map , "announce-list" ) ;
-
-
if ( pAnyNode_list != NULL) // "announce-list" exists in .torrent file
-
{
-
ListNode *pAnnounceList = dynamic_cast<ListNode*>(pAnyNode_list) ;
-
-
// call get_announce_list method
-
get_announce_list ( pAnnounceList , torrent_structure);
-
-
}
-
-
else
-
{
-
LOG(INFO)<<"[info] no key words : exists in torrent file" ;
-
}
-
-
// key word : "info" ; type : dictionary
-
// if single file mode , dictionary elements type : string
-
// if multi-file mode , dictionary elements type : string , list < element type : dictinary >
-
// dictionary element type : string<length>
-
// list <element type : string>
-
//
-
-
{
-
-
// first get info|type : dict ; from pFileDict
-
AnyNode *pAnyNode_dict = find_target_node ( pFileDict->_value_map , "info") ;
-
-
if ( pAnyNode_dict != NULL ) // get "info" key word
-
{
-
DictNode *pInfoDict = dynamic_cast<DictNode*>(pAnyNode_dict) ;
-
-
get_info_dict ( pInfoDict, torrent_structure ) ;
-
}
-
else
-
{
-
LOG(WARNING)<<"[warnning] .torrent file can not find info ";
-
return true ;
-
}
-
-
} ///info parse end
-
-
-
delete pFileDict ;
-
return true ;
-
}
-
-
void TorrentFile::show_torrent_content ( torrent_file_t &torrent_structure)
-
{
-
-
cout << "torrent file contents after analyze"<<endl ;
-
cout << "announce: "<< torrent_structure.announce << endl ;
-
cout << "created by: "<< torrent_structure.created_by<< endl ;
-
cout << "creation date(s):"<< torrent_structure.creation_date << endl ;
-
cout << "comment :"<< torrent_structure.comment << endl ;
-
cout << "encoding : "<< torrent_structure.encoding << endl ;
-
cout << "announce-list:"<<endl ;
-
-
for ( vector<string>::iterator it = torrent_structure.announce_list.begin () ;
-
it != torrent_structure.announce_list.end () ; it++ )
-
{
-
cout << *it << endl ;
-
}
-
-
cout << "info"<< endl;
-
cout << "info.pieces" << torrent_structure.info.pieces << endl ;
-
cout << "info.piece_length" << torrent_structure.info.piece_length << endl ;
-
cout << "info.is_multi_file ? " << endl ;
-
-
if ( torrent_structure.info.is_multi_file )
-
{
-
cout << "yes , it is multi file mode" << endl ;
-
cout << "here are the sub file's path and length" <<endl ;
-
-
for ( vector<file_t>::iterator it = torrent_structure.info.file_list.begin () ;
-
it != torrent_structure.info.file_list.end () ; it++ )
-
{
-
cout << "sub file length "<< it->file_length << endl ;
-
cout << "sub file path " << it->file_path << endl ;
-
}
-
}
-
else
-
{
-
cout << "no , it is single file mode " << endl ;
-
-
cout << "here is the single file's path and length " <<endl ;
-
cout << "single file length "<< torrent_structure.info.file_list[0].file_length << endl ;
-
cout << "single file path(name) " << torrent_structure.info.file_list[0].file_path << endl ;
-
}
-
-
-
}
阅读(1271) | 评论(0) | 转发(0) |