GRIB API源码分析——grib_count实现简析

目录

(源码基于GRIB API 1.11.0版本)

我还没有看懂所有代码,只能简要分析下grib_count的核心算法。GRIB API TOOL使用的C语言编程风格我还得深入研究下。grib_count没有使用grib api tools的通用框架,顶层流程比较直观。
grib_count命令用于统计给出文件中grib消息的总数。源代码在grib_api-1.11.0\tools\grib_count.c。
main函数中对每个文件调用check_file函数,获取每个文件的消息数。
[cpp]
static int check_file(FILE* in,long *count)
[/cpp]
check_file中使用循环从文件中读取grib消息,直到文件结尾。使用grib_read_any_from_file_alloc读取消息,使用grib_content_free释放消息。

grib_content_free

[cpp]
void grib_context_free(const grib_context* c, void* p)
[/cpp]
使用grib_context中设置的释放函数来释放p占的空间。p在check_file中用于缓存读取的文件内容。

grib_read_any_from_file_alloc

在 \src\grib_io.c文件中
[cpp]
int grib_read_any_from_file_alloc(grib_context* ctx,FILE* f,void **buffer,size_t* length)
[/cpp]
该函数仅包装一个reader,传递给read_any函数,获取read_any函数返回的buffer和length。

read_any

同样在\src\grib_io.c文件中
[cpp]
static int read_any(reader *r,int grib_ok,int bufr_ok)
[/cpp]
read_any函数根据数据格式,循环读取文件内容。
首先读取头几个字节,判断文件格式。GRIB文件以四字节的“GRIB”开头。
后续调用格式相关的read函数,如grib文件调用read_GRIB函数。

read_GRIB

[cpp]
static int read_GRIB(reader* r)
[/cpp]
首先确定GRIB版本号。我只关注GRIB2格式的文件,略去GRIB1文件。
接着获取GRIB消息的长度。GRIB2中长度字段占八字节。GRIB API使用long类型表示消息长度,但不同系统long所占字节不同,所以需要检查long所占的位数(不依赖机器的类型在哪里?)。
当sizeof(long)>=8时,直接读取八字节的GRIB消息长度字段
[cpp]
if(sizeof(long) >= 8) {
for(j=0;j<8;j++) { if(r->read(r->read_data,&tmp[i],1,&err) != 1 || err)
return err;
length <<= 8; length |= tmp[i]; i++; } } [/cpp] 若long小于8字节,则消息长度可能会超出long的最大值,这就需要检查该消息的长度是否超过四字节,超过则无法继续进行。 [cpp] /* Check if the length fits in a long */ for(j=0;j<4;j++) { if(r->read(r->read_data,&tmp[i],1,&err) != 1 || err)
return err;
length <<= 8; length |= tmp[i]; i++; } if(length) return GRIB_MESSAGE_TOO_LARGE; /* Message too large */ [/cpp] 然后读取后四字节作为长度。 获得长度后,使用read_the_rest函数读入当前消息剩下的内容。

read_the_rest

同在\src\grib_io.c文件中
[cpp]
static int read_the_rest(reader* r,size_t message_length,unsigned char* tmp, int already_read)
[/cpp]
读取消息中剩下的内容,检测最后是否以”7777″结尾。
将已读入的内容放入buffer。
[cpp]
memcpy(buffer,tmp,already_read);
[/cpp]
读取剩余内容。
[cpp]
if((r->read(r->read_data,buffer+already_read,rest,&err) != rest) || err)
return err;
[/cpp]
检测最后四个字节。
[cpp]
if(!r->headers_only && (buffer[message_length-4] != ‘7’ ||
buffer[message_length-3] != ‘7’ ||
buffer[message_length-2] != ‘7’ ||
buffer[message_length-1] != ‘7’)) {
return GRIB_WRONG_LENGTH;
}
return GRIB_SUCCESS;
[/cpp]
注:令我不解的是,在read_the_rest中已经设置buffer_size = message_length,再检测buffer_size < message_length,是否没有必要?