金鳞化龙 - 老白


智者创造机会,强者把握机会,弱者等待机会~
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

转:关于QQwry格式

Posted on 2005-12-13 11:26  秋衫客  阅读(757)  评论(0)    收藏  举报
这是从cnss的BLOG上原文转下来的,留做参考

作者  cnss 2004-8-18
版权所有 转载请注明出处
http://blog.csdn.net/cnss

最近写了个比qqwry好的格式,点这里查看.

刚才通过RSS看到一篇关于QQwry格式的blog: http://blog.csdn.net/taft/archive/2004/08/18/77559.aspx

想不到QQwry还在用,这是俺两年前设计的,这个格式该被淘汰了.为什么这么说呢,因为它采用的是索引+二分查找来减小内存占用和提高查找速度.

由于采用二分查找,所以IP数据要被分为最小的片,假设有A,B两条数据,B数据完全覆盖A数据,那么转换为QQwry后两条数据就变成了三条.如果原始数据非常有条理,就可以避免这个现象,不过这是不可能的,几万条数据会越来越乱,所以QQwry的尺寸会迅速增加,之所以增长的不是特别快,是因为格式对重复数据有一定压缩.

QQwry.dat:"咦?我没吃那么多,怎么胖的那么快!?"

那篇文章是作者猜测的格式,我再把原来整理的发一遍吧.0x2 0x0 0x0 0x0不是错误,可能是给御风而行放版权信息的地方,另外附带ipsearcher.dll源码.

-----------------------------------------------
新格式说明

主要分为数据区和索引区
★数据区元素:
存放IP信息中的:结束IP(4字节),国家(不定长),地区(不定长)
排列顺序:无要求
★索引区元素:
存放IP信息中的:起始IP(4字节),索引值(3字节)
排列顺序:起始IP按升序排列
★IP为4字节,如"255.0.0.0"表示为0xFF000000,存在文件中则为00 00 00 FF(字节序原因)
★索引值为该IP消息的<结束IP、国家、地区>在文件中的位置。指向<结束IP>
★如果结束IP后的字节为0x01,则说明该IP消息的<国家、地区>与前面的IP信息重复,这时0x01后面的3个字节为国家、地区字符串的偏移量。可以根据这三个字节去前面找国家、地区。
★如果国家的第一个字节为0x02,说明该国家串与前面的国家或地区串重复,0x02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。
★如果地区的第一个字节为0x02,说明该地区串与前面的国家或地区串重复,0x02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。
★有可能在出现0x01的情况下出现0x02,这时需要跳转两次查找国家、地区字符串。
★正常的字符串以NULL做结尾。
★IP信息不允许有重复、覆盖
★使用索引是为了保证能以线性速度搜索
★新格式不允许为未知数据的IP消息,原格式中的未知数据已经都去掉了。如果有未知数据的IP信息,将大大增加文件长度。

文件的头4个字节是索引区第一个元素的偏移量,第二个4字节是索引区最后一个元素的偏移量。通过这两个偏移量,可以用二分法快速查找IP信息。如:一条IP信息是,要查询的IP为150
起始 结束  国家 地区
100  200 中国 北京
首先在索引区找到起始IP小于150的最后一个元素,通过索引,找到结束IP,如果150大于结束IP,说明是未知数据;如果150小于等于结束IP,则找到国家、地区。

// ipsearcher.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include 
<windows.h>
#include 
<stdlib.h> 

extern "C" __declspec(dllexport) void* __cdecl _GetAddress(const char *IPstr);

void *ret[2];           //for return
char *ptr = NULL;       //ptr of image
char *= NULL;         //point to index
unsigned int total;     //ip count

inline unsigned 
int get_3b(const char *mem)
{
    
return 0x00ffffff & *(unsigned int*)(mem);
}

inline 
void Load(void)
{
    HANDLE hnd;      
//file handle
    DWORD NumberOfBytesRead; //len
    char text[2048];  //patch
    char *temp;
    unsigned 
int len;

    
//get patch
    if!GetModuleFileName(0, text, 2048) )
        
return;
    temp 
= strrchr(text, 92);  // 92 = '\'
    *(temp + 1= NULL;
    strcat(temp, 
"QQwry.dat");

    
//CreateFile
    hnd = CreateFile(text, GENERIC_READ, FILE_SHARE_READ, NULL, 
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
if(INVALID_HANDLE_VALUE == hnd)
    {
        ::MessageBox(NULL, text, 
"不能打开文件!", NULL);
        
return;
    }

    
//get len
    len = SetFilePointer(hnd, NULL, NULL, FILE_END);
    SetFilePointer(hnd, NULL, NULL, FILE_BEGIN);

    
//malloc
    ptr = (char*)malloc(len+9);
    
if(!ptr)
    {
        CloseHandle(hnd);
        ::MessageBox(NULL, 
"不能分配内存!", NULL, NULL);
        
return;
    }

    
//read
    if(!ReadFile(hnd, ptr, len, &NumberOfBytesRead, NULL))
    {
        CloseHandle(hnd);
        free(ptr);
        ::MessageBox(NULL, text, 
"不能读入文件!", NULL);
        
return;
    }
    CloseHandle(hnd);

    
//calc total - 1
    total = (*((unsigned int*)ptr+1- *(unsigned int*)ptr);

    
//check file
    if(total % 7 != 0)
    {
        free(ptr);
        ::MessageBox(NULL, text, 
"QQwry.dat文件有损坏!", NULL);
        
return;
    }

    total 
/= 7;
    
++total;
    p 
= ptr + *(unsigned int*)ptr;  //ptr of index area
}

inline unsigned 
int str2ip(const char *lp)
{
    unsigned 
int ret = 0;
    unsigned 
int now = 0;

    
while(*lp)
    {
        
if('.' == *lp)
        {
            ret 
= 256 * ret + now;
            now 
= 0;
        }
        
else
            now 
= 10 * now + *lp - '0';
        
++lp;
    }

    ret 
= 256 * ret + now;
    
return ret;
}

void* __cdecl _GetAddress(const char *IPstr)
{
    
if(NULL == p)
    {
        ret[
0= "无法打开数据";
        ret[
1= "";
        
return ret;
    }

    unsigned 
int ip = str2ip(IPstr);
    
char *now_p;

    unsigned 
int begin = 0, end = total;
    
while(1)
    {
        
if( begin >= end - 1 )
            
break;
        
if( ip < *(unsigned int*)(p + (begin + end)/2 * 7) )
            end 
= (begin + end)/2;
        
else
            begin 
= (begin + end)/2;
     }

    unsigned 
int temp = get_3b(p + 7 * begin + 4);
    
if(ip <= *(unsigned int*)(ptr + temp)) //ok, found
    {
        now_p 
= ptr + temp + 4;
        
if0x01 == *now_p )
            now_p 
= ptr + get_3b(now_p + 1);
        
//country
        if0x02 == *now_p ) //jump
        {
            ret[
0= ptr + get_3b(now_p + 1);
            now_p 
+= 4;
        }
        
else
        {
            ret[
0= now_p;
            
for(; *now_p; ++now_p)
                ;
            
++now_p;
        }
         
//local
         if0x02 == *now_p ) //jump
            ret[1= ptr + get_3b(now_p + 1);
        
else
            ret[
1= now_p;
    }
     
else
    {
        ret[
0= "未知数据";
        ret[
1= "";
    }
    
return ret;
}
BOOL APIENTRY DllMain( HANDLE hModule, 
       DWORD  ul_reason_for_call, 
       LPVOID lpReserved
       )
{
    
switch(ul_reason_for_call)
    {
        
//case DLL_THREAD_ATTACH:
        
//case DLL_THREAD_DETACH:
        case DLL_PROCESS_ATTACH:  //attach
        {
            Load();
        } 
        
break;
        
//------------------------------------
        case DLL_PROCESS_DETACH:  //detach
        {
            free(ptr); 
        }
    }
    
return true;
}