diff --git a/binding/c_mmap/ip2region.c b/binding/c_mmap/ip2region.c new file mode 100644 index 0000000..df63dcc --- /dev/null +++ b/binding/c_mmap/ip2region.c @@ -0,0 +1,411 @@ +/** + * default ip2region implementation + * + * @see #ip2region.h + * @author chenxin + * @date 2015-10-30 +*/ + +#include "ip2region.h" +#include +#include +#include +#include + +/** + * create a new ip2region object + * + * @param dbFile path +*/ +IP2R_API uint_t ip2region_create(ip2region_t ip2rObj, char *dbFile) +{ + struct stat st; + + memset(ip2rObj, 0x00, sizeof(ip2region_entry)); + ip2rObj->dbFile = IP2R_MALLOC(1024); + strcpy(ip2rObj->dbFile, dbFile); + + int fd = open(dbFile, O_RDONLY); + if ( fd < 0 ) { + //fprintf(stderr, "Fail to open the db file %s\n", ip2rObj->dbFile); + return 0; + } + + if ( fstat(fd, &st) < 0 ) { + //fprintf(stderr, "Fail to stat the db file %s\n", ip2rObj->dbFile); + return 0; + } + + ip2rObj->dbSize = st.st_size; + ip2rObj->dbBinStr = mmap(NULL, ip2rObj->dbSize, PROT_READ, MAP_PRIVATE, fd, 0); + if ( ip2rObj->dbBinStr == (void *) -1 ) { + //fprintf(stderr, "Fail to map the db file %s\n", ip2rObj->dbFile); + return 0; + } + + // Close db file as soon as possible. + close(fd); + + ip2rObj->headerLen = 0; + ip2rObj->HeaderSip = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH); + if ( ip2rObj->HeaderSip == NULL ) { + return 0; + } + + ip2rObj->HeaderPtr = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH); + if ( ip2rObj->HeaderPtr == NULL ) { + IP2R_FREE(ip2rObj->HeaderSip); + return 0; + } + + ip2rObj->firstIndexPtr = getUnsignedInt(ip2rObj->dbBinStr, 0); + ip2rObj->lastIndexPtr = getUnsignedInt(ip2rObj->dbBinStr, 4); + ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1; + + return 1; +} + +/** + * destroy the specifield ip2region object + * + * @param ip2region_t +*/ +IP2R_API uint_t ip2region_destroy(ip2region_t ip2rObj) +{ + IP2R_FREE(ip2rObj->HeaderSip); + ip2rObj->HeaderSip = NULL; + IP2R_FREE(ip2rObj->HeaderPtr); + ip2rObj->HeaderPtr = NULL; + + //free the db binary string + if ( ip2rObj->dbBinStr != NULL ) { + munmap(ip2rObj->dbBinStr, ip2rObj->dbSize); + ip2rObj->dbBinStr = NULL; + } + + return 1; +} + +/** + * get the region associated with the specified ip address with the memory binary search algorithm + * + * @param ip2rObj + * @param ip + * @param datablock +*/ +IP2R_API uint_t ip2region_binary_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock) +{ + int l, h, m, p; + uint_t sip, eip, dptr; + int dataLen, dataptr; + long filesize; + char *buffer; + + l = 0; h = ip2rObj->totalBlocks; dptr = 0; + while ( l <= h ) { + m = (l + h) >> 1; + p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH; + + buffer = ip2rObj->dbBinStr + p; + sip = getUnsignedInt(buffer, 0); + if ( ip < sip ) { + h = m - 1; + } else { + eip = getUnsignedInt(buffer, 4); + if ( ip > eip ) { + l = m + 1; + } else { + dptr = getUnsignedInt(buffer, 8); + break; + } + } + } + + if ( dptr == 0 ) return 0; + + //get the data + dataLen = ((dptr >> 24) & 0xFF); + dataptr = (dptr & 0x00FFFFFF); + buffer = ip2rObj->dbBinStr + dataptr; + + //fill the data to the datablock + datablock->city_id = getUnsignedInt(buffer, 0); + dataLen -= 4; //reduce the length of the city_id + memcpy(datablock->region, buffer + 4, dataLen); + datablock->region[dataLen] = '\0'; + + return 1; +} + +IP2R_API uint_t ip2region_binary_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock) +{ + return ip2region_binary_search(ip2rObj, ip2long(ip), datablock); +} + +/** + * get the region associated with the specifield ip address with file binary search algorithm + * + * @param ip2rObj + * @param ip + * @param datablock + * @return uint_t +*/ +IP2R_API uint_t ip2region_file_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock) +{ + int l, h, m, p; + uint_t sip, eip, dptr; + char buffer[256]; + int dataLen, dataptr; + + FILE *dbHandler = fopen(ip2rObj->dbFile, "rb"); + if ( dbHandler == NULL ) { + return 0; + } + fseek(dbHandler, 0, 0); + if ( fread(buffer, 8, 1, dbHandler) != 1 ) { + return 0; + } + + ip2rObj->firstIndexPtr = getUnsignedInt(buffer, 0); + ip2rObj->lastIndexPtr = getUnsignedInt(buffer, 4); + ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1; + + //binary search the index blocks to define the data block + l = 0; h = ip2rObj->totalBlocks; dptr = 0; + while ( l <= h ) { + m = (l + h) >> 1; + p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH; + + fseek(dbHandler, p, 0); + if ( fread(buffer, INDEX_BLOCK_LENGTH, 1, dbHandler) != 1 ) { + return 0; + } + + sip = getUnsignedInt(buffer, 0); + if ( ip < sip ) { + h = m - 1; + } else { + eip = getUnsignedInt(buffer, 4); + if ( ip > eip ) { + l = m + 1; + } else { + dptr = getUnsignedInt(buffer, 8); + break; + } + } + } + + if ( dptr == 0 ) return 0; + + //get the data + dataLen = ((dptr >> 24) & 0xFF); + dataptr = (dptr & 0x00FFFFFF); + + //memset(data, 0x00, sizeof(data)); + fseek(dbHandler, dataptr, 0); + if ( fread(buffer, dataLen, 1, dbHandler) != 1 ) { + return 0; + } + + //fill the data to the datablock + datablock->city_id = getUnsignedInt(buffer, 0); + dataLen -= 4; //reduce the length of the city_id + memcpy(datablock->region, buffer + 4, dataLen); + datablock->region[dataLen] = '\0'; + + if ( dbHandler != NULL ) { + fclose(dbHandler); + } + + return 1; +} + +IP2R_API uint_t ip2region_file_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock) +{ + return ip2region_file_search(ip2rObj, ip2long(ip), datablock); +} + +/** + * get the region associated with the specifield ip address with b-tree algorithm + * + * @param ip2rObj + * @param ip + * @param datablock + * @return uint_t +*/ +IP2R_API uint_t ip2region_btree_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock) +{ + int i, j; + int l, m, h, p, sptr, eptr, indexBlockLen, dataLen, dataptr; + uint_t sip, eip, idxptr, dptr; + char *buffer; + + if ( ip2rObj->headerLen == 0 ) { + buffer = ip2rObj->dbBinStr + 8; + for ( i = j = 0; i < TOTAL_HEADER_LENGTH; i += 8, j++ ) { + sip = getUnsignedInt(buffer, i); + idxptr = getUnsignedInt(buffer, i + 4); + if ( idxptr == 0 ) break; + + ip2rObj->HeaderSip[j] = sip; + ip2rObj->HeaderPtr[j] = idxptr; + } + + ip2rObj->headerLen = j; + } + + //search the header block to define the index block + l = 0; h = ip2rObj->headerLen; sptr = 0; eptr = 0; + while ( l <= h ) { + m = ((l + h) >> 1); + + //perfetc matched, just return it + if ( ip == ip2rObj->HeaderSip[m] ) { + if ( m > 0 ) { + sptr = ip2rObj->HeaderPtr[m-1]; + eptr = ip2rObj->HeaderPtr[m ]; + } else { + sptr = ip2rObj->HeaderPtr[m ]; + eptr = ip2rObj->HeaderPtr[m+1]; + } + + break; + } + + //less then the middle value + if ( ip < ip2rObj->HeaderSip[m] ) { + if ( m == 0 ) { + sptr = ip2rObj->HeaderPtr[m ]; + eptr = ip2rObj->HeaderPtr[m+1]; + break; + } else if ( ip > ip2rObj->HeaderSip[m-1] ) { + sptr = ip2rObj->HeaderPtr[m-1]; + eptr = ip2rObj->HeaderPtr[m ]; + break; + } + h = m - 1; + } else { + if ( m == ip2rObj->headerLen - 1 ) { + sptr = ip2rObj->HeaderPtr[m-1]; + eptr = ip2rObj->HeaderPtr[m ]; + break; + } else if ( ip <= ip2rObj->HeaderSip[m+1] ) { + sptr = ip2rObj->HeaderPtr[m ]; + eptr = ip2rObj->HeaderPtr[m+1]; + break; + } + l = m + 1; + } + } + + //not matched just stop it + if ( sptr == 0 ) return 0; + + indexBlockLen = eptr - sptr; + buffer = ip2rObj->dbBinStr + sptr; + + dptr = 0; l = 0; h = indexBlockLen / INDEX_BLOCK_LENGTH; + while ( l <= h ) { + m = ((l + h) >> 1); + p = m * INDEX_BLOCK_LENGTH; + sip = getUnsignedInt(buffer, p); + if ( ip < sip ) { + h = m - 1; + } else { + eip = getUnsignedInt(buffer, p + 4); + if ( ip > eip ) { + l = m + 1; + } else { + dptr = getUnsignedInt(buffer, p + 8); + break; + } + } + } + + if ( dptr == 0 ) return 0; + + dataLen = ((dptr >> 24) & 0xFF); + dataptr = (dptr & 0x00FFFFFF); + buffer = ip2rObj->dbBinStr + dataptr; + + datablock->city_id = getUnsignedInt(buffer, 0); + dataLen -= 4; + memcpy(datablock->region, buffer + 4, dataLen); + datablock->region[dataLen] = '\0'; + + return 1; +} + +IP2R_API uint_t ip2region_btree_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock) +{ + return ip2region_btree_search(ip2rObj, ip2long(ip), datablock); +} + +/** + * get a unsinged long(4bytes) from a specifield buffer start from the specifield offset + * + * @param buffer + * @param offset + * @return uint_t +*/ +IP2R_API uint_t getUnsignedInt(char *buffer, int offset) +{ + return ( + ((buffer[offset ]) & 0x000000FF) | + ((buffer[offset+1] << 8) & 0x0000FF00) | + ((buffer[offset+2] << 16) & 0x00FF0000) | + ((buffer[offset+3] << 24) & 0xFF000000) + ); +} + +/** + * string ip to long + * + * @param ip + * @return uint_t +*/ +IP2R_API uint_t ip2long(char *ip) +{ + int i = 0, p = 24; + char buffer[4], *cs = ip; + uint_t ipval = 0; + + while ( *cs != '\0' ) { + if ( *cs == '.' ) { + //single part length limit + if ( i > 3 ) { + ipval = 0; + break; + } + + if ( p < 0 ) break; + buffer[i] = '\0'; + ipval |= (atoi(buffer) << p); + p -= 8; + i = 0; + } else { + buffer[i++] = *cs; + } + + cs++; + } + + //append the rest parts + if ( i > 3 ) return 0; + buffer[i] = '\0'; + ipval |= atoi(buffer); + + return ipval; +} + +/** + * long to string ip + * + * @param ip + * @param buffer + * @return uint_t(1 for success and 0 for failed) +*/ +IP2R_API uint_t long2ip(uint_t ip, char *buffer) +{ + return 0; +} diff --git a/binding/c_mmap/ip2region.h b/binding/c_mmap/ip2region.h new file mode 100644 index 0000000..d84b173 --- /dev/null +++ b/binding/c_mmap/ip2region.h @@ -0,0 +1,149 @@ +/** + * ip2region header file + * + * @author chenxin +*/ + +#ifndef _IP2REGION_H +#define _IP2REGION_H + +#include +#include +#include + +//yat, just take it as this way, 99 percent and they works find +#if ( defined(_WIN32) || defined(_WINDOWS_) || defined(__WINDOWS_) ) +# define OS_WINDOW +#else +# define OS_LINUX +#endif + +#ifdef OS_WINDOW +# define IP2R_API extern __declspec(dllexport) +#else +/*platform shared library statement :: unix*/ +# define IP2R_API extern +#endif + +#define print(str) printf("%s", str) +#define println(str) printf("%s\n", str) + +/* + * memory allocation macro definition. + * cause we should use emalloc,ecalloc .ege. in php. + * so you could make it better apdat the php environment. + */ +#define IP2R_CALLOC(_bytes, _blocks) calloc(_bytes, _blocks) +#define IP2R_MALLOC(_bytes) malloc(_bytes) +#define IP2R_FREE(_ptr) free(_ptr) + +typedef unsigned short ushort_t; +typedef unsigned char uchar_t; +typedef unsigned int uint_t; +typedef unsigned long ulong_t; + +#define INDEX_BLOCK_LENGTH 12 +#define TOTAL_HEADER_LENGTH 4096 + +/* + * ip2region properties struct +*/ +typedef struct { + uint_t *HeaderSip; //header start ip blocks + uint_t *HeaderPtr; //header ptr blocks + uint_t headerLen; //header block number + char *dbFile; //path of db file + char *dbBinStr; //db binary string for memory search mode + uint_t dbSize; + uint_t firstIndexPtr; //first index ptr + uint_t lastIndexPtr; //last index ptr + uint_t totalBlocks; //total index blocks number +} ip2region_entry; +typedef ip2region_entry * ip2region_t; + +/* + * data block +*/ +typedef struct { + uint_t city_id; + char region[256]; +} datablock_entry; +typedef datablock_entry * datablock_t; + + +/** + * create a new ip2region object + * + * @param ip2rObj + * @param dbFile path +*/ +IP2R_API uint_t ip2region_create(ip2region_t, char *); + +/** + * destroy the specified ip2region object + * + * @param ip2region_t +*/ +IP2R_API uint_t ip2region_destroy(ip2region_t); + +/** + * get the region associated with the specified ip address with memory binary search algorithm + * + * @param ip2region_t + * @param uint_t + * @param datablock_t + * @date 2016/06/30 +*/ +IP2R_API uint_t ip2region_binary_search(ip2region_t, uint_t, datablock_t); +IP2R_API uint_t ip2region_binary_search_string(ip2region_t, char *, datablock_t); + + +/** + * get the region associated with the specified ip address with file binary search algorithm + * + * @param ip2rObj + * @param ip + * @param datablock + * @return uint_t +*/ +IP2R_API uint_t ip2region_file_search(ip2region_t, uint_t, datablock_t); +IP2R_API uint_t ip2region_file_search_string(ip2region_t, char *, datablock_t); + +/** + * get the region associated with the specified ip address with b-tree algorithm + * + * @param ip2rObj + * @param ip + * @param datablock + * @return uint_t +*/ +IP2R_API uint_t ip2region_btree_search(ip2region_t, uint_t, datablock_t); +IP2R_API uint_t ip2region_btree_search_string(ip2region_t, char *, datablock_t); + +/** + * get a unsinged long(4bytes) from a specified buffer start from the specified offset + * + * @param buffer + * @param offset + * @return uint_t +*/ +IP2R_API uint_t getUnsignedInt(char *, int); + +/** + * string ip to long + * + * @param ip + * @return uint_t +*/ +IP2R_API uint_t ip2long(char *); + +/** + * long to string ip + * + * @param ip + * @param buffer + * @return uint_t(1 for success and 0 for failed) +*/ +IP2R_API uint_t long2ip(uint_t, char *); + +#endif /*end ifndef*/ diff --git a/binding/c_mmap/testSearcher.c b/binding/c_mmap/testSearcher.c new file mode 100644 index 0000000..a8c074a --- /dev/null +++ b/binding/c_mmap/testSearcher.c @@ -0,0 +1,102 @@ +/** + * test ip2region searcher program + * + * @author chenxin + * @date 2015-10-30 +*/ + +#include "ip2region.h" +#include +#include +#include +#include + +#define __PRINT_ABOUT__ \ + println("+-------------------------------------+"); \ + println("| ip2region test program |"); \ + println("| Author: chenxin619315@gmail.com. |"); \ + println("| Type 'quit' to exit the program. |"); \ + println("+-------------------------------------+"); + +//read a line from a command line. +static char *getLine( FILE *fp, char *__dst ) +{ + register int c; + register char *cs; + + cs = __dst; + while ( ( c = getc( fp ) ) != EOF ) { + if ( c == '\n' ) break; + *cs++ = c; + } + *cs = '\0'; + + return ( c == EOF && cs == __dst ) ? NULL : __dst; +} + +static double getTime() +{ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + + return (tv.tv_sec * 1000 + ((double)tv.tv_usec)/1000); +} + +int main( int argc, char **argv ) +{ + ip2region_entry ip2rEntry; + datablock_entry datablock; + char *dbFile = NULL, *algorithm = NULL; + char line[256]; + uint_t (*func_ptr)(ip2region_t, char *, datablock_t); + double s_time, c_time; + memset(&datablock, 0x00, sizeof(datablock_entry)); + + if ( argc < 2 ) { + printf("Usage: a.out [ip2region db file path] [algorithm]\n"); + return 0; + } + + dbFile = argv[1]; + algorithm = "B-tree"; + func_ptr = ip2region_btree_search_string; + if ( argc >= 3 ) { + if ( strcmp(argv[2], "binary") == 0 ) { + algorithm = "File"; + func_ptr = ip2region_binary_search_string; + } else if ( strcmp(argv[2], "file") == 0 ) { + algorithm = "File"; + func_ptr = ip2region_file_search_string; + } + } + + //create a new ip2rObj + printf("+--initializing %s ... \n", algorithm); + if ( ip2region_create(&ip2rEntry, dbFile) == 0 ) { + println("Error: Fail to create the ip2region object\n"); + return 0; + } + + __PRINT_ABOUT__; + + while ( 1 ) { + print("ip2region>> "); + getLine( stdin, line ); + if ( strlen(line) < 2 ) continue; + if ( strcasecmp( line, "quit" ) == 0 ) { + println("+--Bye!"); + break; + } + + s_time = getTime(); + func_ptr(&ip2rEntry, line, &datablock); + c_time = getTime() - s_time; + printf("%d|%s in %.5f millseconds\n", datablock.city_id, datablock.region, c_time); + } + + //destory the ip2rObj + ip2region_destroy(&ip2rEntry); + + return 0; +}