o5mparser

解析o5m文件,在命令行中显示的c程序
共741行
下一步应整合、简化代码,规范接口,转化为C++程序

// o5m_demo 2012-10-14 17:10
// (c) 2012 Markus Weber, Nuernberg
//
// 这个程序从标准输入读取.o5m文件,并使用简单的文本格式将其内容写入标准输出。
// 这是如何读取.o5m文件的一个实验性例子。
//
// 编译这个文件:
// gcc o5m_demo.c -o o5m_demo
//
#define _FILE_OFFSET_BITS 64
#include   //提供各种进制的转换宏
#include  //申请、释放内存空间
#include 
#include 
#include 
#include  // 是POSIX标准定义的unix类系统定义符号常量的头文件
#include  // 用来避免一些系统安全问题

// typedef enum {false= 0,true= 1} bool; // 一般现在无需定义
typedef uint8_t byte; // 一字节
#if !__WIN32__
  #define O_BINARY 0
#endif
#define ONAME(i) \
  (i==0? "node": i==1? "way": i==2? "relation": "unknown object")

static inline char *stpcpy0(char *dest, const char *src) {
  // 重新定义C99的stpcpy(),因为它在MinGW中是缺少的,
  // 而在Linux头文件中的声明似乎是错误的;
  while(*src!=0)
    *dest++= *src++;
  *dest= 0;
  return dest;
  }  // stpcpy0()

static inline int strzcmp(const char* s1,const char* s2) {
  // 类似于strcmp(),这个过程比较两个字符串;
  // 在这里,要比较的字符数限于第二个字符串的长度;
  // 即,该过程可以用来识别长字符串s1内的短字符串s2;
  // s1[]: 第一个字符串;
  // s2[]: 与第一个字符串作比较的字符串;
  // 返回:
  // 0: 两个字符串是相同的;第一串可以比第二串长;
  // -1: 第一个字符串比第二个字母小;
  // 1: 第一个字符串比第二个字符串更大;
  while(*s1==*s2 && *s1!=0) { s1++; s2++; }
  if(*s2==0)
    return 0;
  return *(unsigned char*)s1 < *(unsigned char*)s2? -1: 1;
}  // strzcmp()
static inline char *strmcpy(char *dest, const char *src, size_t maxlen) {
  // 类似于strcpy(),这个过程 复制 一个字符串;
  // 这里是关心长度的, i.e. 如果太长,目标字符串将受到限制;
  // src[]: 要复制的源字符串;
  // maxlen: 目标字符串的最大长度(包括终止符null)
  // return:
  // dest[]: 目标字符串; 这也是函数的返回值
  char* d;

  if(maxlen==0)
return dest;
  d= dest;
  while(--maxlen>0 && *src!=0)
    *d++= *src++;
  *d= 0;
  return dest;
  }  // end   strmcpy()
#define strMcpy(d,s) strmcpy((d),(s),sizeof(d))


//------------------------------------------------------------
// Module pbf_   protobuf转换模块
//------------------------------------------------------------

// 这个模块提供了从protobuf格式到常规数字转换的程序;
// 像往常一样,一个模块的所有标识符都有相同的前缀;
// 此时是 'pbf'; 在全局可访问的对象中将有一个下划线
// 在本模块之外不可访问的对象将有两个下划线
// 私有和公共定义的部分用水平线分隔:----
// 许多程序都有一个参数'pp';这里预期会有一个缓冲区指针
// 这个指针会增加被转换的protobuf元素消耗的字节数

//------------------------------------------------------------

static inline uint32_t pbf_uint32(byte** pp) {
  // 获取无符号32位整数的值;
  // pp: see module header;
  byte* p;
  uint32_t i;
  uint32_t fac;

  p= *pp;
  i= *p;
  if((*p & 0x80)==0) {  // 只有一比特 (高位为0,代表只有一比特)
    (*pp)++; // 指针后移
return i; // 返回获取的整数值
    }
  i&= 0x7f;
  fac= 0x80;
  while(*++p & 0x80) {  // 更多的比特
    i+= (*p & 0x7f)*fac; // 计算整数值
    fac<<= 7;
    }
  i+= *p++ *fac;
  *pp= p;
  return i;
  }  // pbf_uint32()

static inline int32_t pbf_sint32(byte** pp) {
  // 获取有符号32位整数的值;
  // pp: see module header;
  byte* p;
  int32_t i;
  int32_t fac;
  int sig;

  p= *pp;
  i= *p;
  if((*p & 0x80)==0) {  // 只有一比特
    (*pp)++;
    if(i & 1)  // 负数
return -1-(i>>1);
    else
return i>>1;
    }
  sig= i & 1; // 符号位
  i= (i & 0x7e)>>1;
  fac= 0x40;
  while(*++p & 0x80) {  // more byte(s) will follow
    i+= (*p & 0x7f)*fac;
    fac<<= 7;
    }
  i+= *p++ *fac;
  *pp= p;
    if(sig)  // 负数
return -1-i;
    else
return i;
  }  // pbf_sint32()

static inline uint64_t pbf_uint64(byte** pp) {
  // 获取无符号64位整数的值;
  // pp: see module header;
  byte* p;
  uint64_t i;
  uint64_t fac;

  p= *pp;
  i= *p;
  if((*p & 0x80)==0) {  // just one byte
    (*pp)++;
  return i;
    }
  i&= 0x7f;
  fac= 0x80;
  while(*++p & 0x80) {  // more byte(s) will follow
    i+= (*p & 0x7f)*fac;
    fac<<= 7;
    }
  i+= *p++ *fac;
  *pp= p;
  return i;
}  // pbf_uint64()

static inline int64_t pbf_sint64(byte** pp) {
  // 获取有符号64位整数的值;
  // pp: see module header;
  byte* p;
  int64_t i;
  int64_t fac;
  int sig;

  p= *pp;
  i= *p;
  if((*p & 0x80)==0) {  // just one byte
    (*pp)++;
    if(i & 1)  // negative
return -1-(i>>1);
    else
return i>>1;
    }
  sig= i & 1;
  i= (i & 0x7e)>>1;
  fac= 0x40;
  while(*++p & 0x80) {  // more byte(s) will follow
    i+= (*p & 0x7f)*fac;
    fac<<= 7;
    }
  i+= *p++ *fac;
  *pp= p;
    if(sig)  // negative
return -1-i;
    else
return i;
  }  // pbf_sint64()

//------------------------------------------------------------
// end   Module pbf_   protobuf转换模块
//------------------------------------------------------------



//------------------------------------------------------------
// Module read_   OSM文件读取模块
//------------------------------------------------------------

// 本模块提供缓冲读取标准输入的程序;
// 像往常一样,一个模块的所有标识符都有相同的前缀;
// 此时是 'read'; 在全局可访问的对象中将有一个下划线
// 在本模块之外不可访问的对象将有两个下划线
// 私有和公共定义的部分用水平线分隔:----
#define read_PREFETCH ((32+3)*1024*1024) // (prefetch)预取 35MB
  // 每次调用 read_input() 后缓冲区中可用的字节数;
#define read__bufM (read_PREFETCH*5)  // 缓冲区的长度 35MBx5
  // 每次调用 read_input() 后缓冲区中可用的字节数;
typedef struct {  // 成员可能无法从外部访问
  FILE* fi;  // 文件流
  off_t jumppos;  // position to jump to; -1: invalid

  int64_t counter;
    // 字节计数器获取输入文件中的读取位置;
  char filename[100];
  bool eof;  // 输文件结尾的标志
  byte* bufp;  // buf[]中的指针
  byte* bufe;  // 指向在buf[]中有效输入结束的指针
  uint64_t bufferstart;
    // 表示读取缓冲区开始的哑变量
    // 与这个读信息结构的实例连接;
  } read_info_t;
static bool read__jumplock= false;  // do not change .jumppos anymore;

//------------------------------------------------------------

static read_info_t* read_infop= NULL;
  // 目前使用的阅读信息结构, 即文件句柄
#define read__buf ((byte*)&read_infop->bufferstart)
  // 文件输入缓冲区的起始地址
static byte* read_bufp= NULL;  
  // 可能在read_input()再次调用之前增加到 read_PREFETCH 字节
static byte* read_bufe= NULL;  // 可能不会从外部改变

//------------------------------------------------------------

static int read_open(const char* filename) {
  // 打开一个输入文件;
  // filename[]: 输入文件的路径和名称;
  //             ==NULL: 标准输入;
  // 返回: 0: 正常; !=0: 错误;
  // read_infop: 文件句柄;
  // 注意你应该在程序结束之前用read_close()关闭每个打开的文件

  // 保存当前处理的输入文件的状态 (if any)
  if(read_infop!=NULL) {
    read_infop->bufp= read_bufp;
    read_infop->bufp= read_bufe;
    }

  // 分配 文件句柄 及 输入缓冲区 的内存空间
  read_infop= (read_info_t*)malloc(sizeof(read_info_t)+read__bufM);
  if(read_infop==NULL) {
    fprintf(stderr,"osmconvert Error: could not get "
      "%i bytes of memory.\n",read__bufM);
return 1;
    }

  // 初始化 read info 结构
  read_infop->fi= NULL;  // (default) file not opened
  strMcpy(read_infop->filename,filename);
  read_infop->eof= false;  // we are not at the end of input file
  read_infop->bufp= read_infop->bufe= read__buf;  // pointer in buf[]
    // pointer to the end of valid input in buf[]
  read_infop->counter= 0;
  read_infop->jumppos= 0;
    // 将文件的起始存储为默认跳转目的地

  // 设置与此文件相关联的模块全局变量
  read_bufp= read_infop->bufp;
  read_bufe= read_infop->bufe;

  // 打开文件
  fprintf(stderr,"Read-opening: %s\n",read_infop->filename);

  if(filename!=NULL) {  // 一个真实的文件应该被打开
    read_infop->fi= fopen(filename,"rb");
    if(read_infop->fi==NULL) {
      fprintf(stderr,
        "osmconvert Error: could not open input file: %.80s\n",
        read_infop->filename);
      free(read_infop); read_infop= NULL;
      read_bufp= read_bufe= NULL;
return 1;
      }
    }  // end   一个真实的文件应该被打开
return 0;
}  // end   read_open()

static void read_close() {
  // 关闭一个打开的文件;
  // read_infop: 将要关文件句柄;
  if(read_infop==NULL)  // 文件句柄不可用;
return;
  fprintf(stderr,"Read-closing: %s\n",read_infop->filename);
  fclose(read_infop->fi);
  free(read_infop); read_infop= NULL;
  read_bufp= read_bufe= NULL;
  }  // end   read_close()
static inline bool read_input() {
  // 从标准输入文件读取数据,使用内部缓冲;
  // 使数据在 read_bufp 可用;
  // 在调用此过程之前,必须调用 read_open() ;
  // read_bufp: 可用的下一个字节开始;
  //            可以由调用者增加到 read_bufe ;
  // read_bufe: 缓冲区中字节的结束;
  //            不能被调用者更改;
  // 在调用此过程之后, 在地址 read_bufp 中至少有 read_PREFETCH 字节可用
  //  - 有一个例外: 如果从标准输入中没有足够的可读取的字节, 
  // 缓冲区中文件结束后的剩余部分直到read_bufp + read_PREFETCH所有字节都将被设置为0x00
  int l,r;

  if(read_bufp+read_PREFETCH>=read_bufe) {  // 读缓冲区过低
    if(!read_infop->eof) {  // still bytes to read
      if(read_bufe>read_bufp) {  // 缓冲区中有剩余的字节
        memmove(read__buf,read_bufp,read_bufe-read_bufp);
          // move remaining bytes to start of buffer
        read_bufe= read__buf+(read_bufe-read_bufp);
          // protect the remaining bytes at buffer start
        }
      else  // 缓冲区中没有剩余字节
        read_bufe= read__buf;  // no bytes remaining to protect
        // add read bytes to debug counter
      read_bufp= read__buf;
      do {  // while buffer has not been filled
        l= (read__buf+read__bufM)-read_bufe-4;
          // number of bytes to read
          r= read(fileno(read_infop->fi),read_bufe,l); // 读l长度的数据保存到缓冲区,返回实际读入的长度
        if(r<=0) {  // no more bytes in the file
          read_infop->eof= true;
            // memorize that there we are at end of file
          l= (read__buf+read__bufM)-read_bufe;
            // remaining space in buffer
          if(l>read_PREFETCH) l= read_PREFETCH;
          memset(read_bufe,0,l);  // 2011-12-24
            // set remaining space up to prefetch bytes in buffer to 0
      break;
          }
        read_infop->counter+= r;
        read_bufe+= r;  // 设置新的数据结尾标志位置
        read_bufe[0]= 0; read_bufe[1]= 0;  // set 4 null-terminators
        read_bufe[2]= 0; read_bufe[3]= 0;
        } while(reof && read_bufp>=read_bufe;
  }  // end   read__input()

//------------------------------------------------------------
// end   Module read_   OSM文件读取模块
//------------------------------------------------------------




//------------------------------------------------------------
// Module str_   字符串读取模块
//------------------------------------------------------------

// 此模块提供了从数据流对象中存储的字符串转换为c格式字符串的过程;
// 像往常一样,一个模块的所有标识符都有相同的前缀,
// 此时为 'str'; 在全局可访问的对象中将有一个下划线
// 在本模块之外不可访问的对象将有两个下划线
// 私有和公共定义的部分用水平线分隔:----

#define str__tabM (15000+4000)
  // +4000是因为可能发生一个对象有很多 key/val 对或者 refroles 的情况
  // 它们没有被存储
#define str__tabstrM 250  // 必须是< str__tab[]的行大小

static char str__tab[str__tabM][256];
  // 字符串表 see o5m documentation ;
  // 行长度必须至少是 str__tabstrM+2 ;
  // 每一行包含一个双字符串; 
  // 两个字符串中的每一个都以零字节结尾,
  // 逻辑长度总共不超过 str__tabstrM 字节;
  // 这个数组的第一个 str__tabM 行被用作字符串的输入缓冲区;
static int str__tabi= 0;
  // 在字符串表中最后输入元素的索引;
static int str__tabn= 0;  // 字符串表中有效字符串的数量;

//------------------------------------------------------------

static inline void str_reset() {
  // 清空字符串表;
  // 必须在本模块的任何其他进程之前调用
  // 可能在任何字符串进程需要重启时调用;
  str__tabi= str__tabn= 0;
  }  // str_reset()

static void str_read(byte** pp,char** s1p,char** s2p) {
  // 从标准输入缓冲中读取一个o5m格式的字符串(对),如. key/val;
  // 如果读取到一个字符串引用,则使用内部字符串表解释它;
  // 如果字符串总长度超过250个字符,则不使用引用(包括终止符为252);
  // pp: 缓冲指针的地址;
  //     这个指针将会增加被转换的protobuf元素消耗的字节数;
  // s2p: ==NULL: 读取的不是字符串对,而是单个字符串;
  // 返回:
  // *s1p,*s2p: 已读取的字符串的指针;
  char* p;
  int len1,len2;
  int ref;
  bool donotstore;  // 字符串“不存储标志”  2012-10-01

  p= (char*)*pp;
  if(*p==0) {  // 直接给出的字符串(对)
    p++;
    donotstore= false;
    #if 0  // 不使用,因为字符串不再是透明的
    if(*++p==(char)0xff) {  // 字符串有“不存储标志”  0xff是重置字节
      donotstore= true;
      p++;
      }  // 字符串有“不存储标志”
      #endif
    *s1p= p;
    len1= strlen(p); // 获取当前字符串长度
    p+= len1+1; // 指针后移到下一个字符串的起始地址
    if(s2p==NULL) {  // 单个字符串
      if(!donotstore && len1<=str__tabstrM) {
          // 单个字符串对于字符串表来说足够短
        stpcpy0(str__tab[str__tabi], *s1p)[1]= 0;
          // 添加第二个终结符,以防万一有人会尝试
      // 将这个单一字符串作为字符串对来读取;
        if(++str__tabi>=str__tabM) str__tabi= 0;
        if(str__tabn=str__tabM) str__tabi= 0;
        if(str__tabnstr__tabn) {  // 无效的字符串引用(因为ref > 字符串表中有效字符串的数量)
      fprintf(stderr,"Invalid .o5m string reference: %i->%i\n",
        str__tabn,ref);
      *s1p= "(invalid)";
      if(s2p!=NULL)  // 调用者需要一个字符串对
        *s2p= "(invalid)";
      }  // 无效的字符串引用
    else {  // 有效的字符串引用
      ref= str__tabi-ref; // 向前找引用的索引
      if(ref<0) ref+= str__tabM;
      *s1p= str__tab[ref];
      if(s2p!=NULL)  // 调用者需要一个字符串对
        *s2p= strchr(str__tab[ref],0)+1; 
    // (strchr: 从字符串 str__tab[ref] 中寻找字符0第一次出现的位置),然后+1,即定位到了下一个字符串
      }  // 有效的字符串引用
    }  // 通过引用给出的字符串(对)
  }  // str_read()

//------------------------------------------------------------
// end   Module str_   字符串读取模块
//------------------------------------------------------------

static inline char* sint32tosfix7(int32_t v,char* sp) {
  // 将sint32作为7位小数定点值并将其转换为字符串;
  // v: 整数值(定点);
  // sp[13]: 目标字符串;
  // 返回: sp;
  char* s1,*s2,c;
  int i;

  s1= sp;
  if(v<0) // 负数
    { *s1++= '-'; v= -v; }
  s2= s1;
  i= 7;
  while((v%10)==0 && i>0)  // trailing zeroes
    { v/= 10;  i--; }
  while(--i>=0)
    { *s2++= (v%10)+'0'; v/= 10; }
  *s2++= '.';
  do
    { *s2++= (v%10)+'0'; v/= 10; }
    while(v>0);
  *s2--= 0;
  while(s2>s1)
    { c= *s1; *s1= *s2; *s2= c; s1++; s2--; }
  return sp;
  }  // sint32tosfix7()

static inline void uint64totimestamp(uint64_t v,char* sp) {
  // 将sint64转换为OSM格式的时间戳,
  // 举例说: "2010-09-30T19:23:30Z";
  // v: 时间戳值;
  // sp[21]: 目标字符串;
  time_t vtime;
  struct tm tm;
  int i;

  vtime= v;
  #if __WIN32__
  memcpy(&tm,gmtime(&vtime),sizeof(tm));
  #else
  gmtime_r(&vtime,&tm);
  #endif
  i= tm.tm_year+1900;
  sp+= 3; *sp--= i%10+'0';
  i/=10; *sp--= i%10+'0';
  i/=10; *sp--= i%10+'0';
  i/=10; *sp= i%10+'0';
  sp+= 4; *sp++= '-';
  i= tm.tm_mon+1;
  *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= '-';
  i= tm.tm_mday;
  *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= 'T';
  i= tm.tm_hour;
  *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= ':';
  i= tm.tm_min;
  *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= ':';
  i= tm.tm_sec%60;
  *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= 'Z'; *sp= 0;
  }  // uint64totimestamp()

//------------------------------------------------------------

static int process_o5m(const char* filename) {
  // 开始处理o5m对象;
  // 返回: ==0: ok; !=0: 错误;
  bool write_testmode;  // 如果为真: 报告未知的 o5m ids
  int otype;  // 当前处理对象的类型;
    // 0: node; 1: way; 2: relation;
  int64_t o5id;  // for o5m delta coding
  int32_t o5lon,o5lat;  // for o5m delta coding
  int64_t o5histime;  // for o5m delta coding
  int64_t o5hiscset;  // for o5m delta coding
  int64_t o5ref[4];  // for o5m delta coding for nodes, ways, relations,
    // and dummy object (just to allow index division by 4)
  bool o5endoffile;  // 文件的逻辑结束(数据集id 0xfe)
  byte* bufp;  // 在读取缓冲区的指针
  byte* bufe;  // 在读取缓冲区的指针, 对象的结束
  byte b;  // 已读取的最新字节
  int l;

  // 程序初始化
  write_testmode= false;  // do not criticize unknown o5m ids
  o5endoffile= false;

  // 读 .o5m 文件头
  read_open(filename);
  read_input();
  bufp= read_bufp;
  if(read_bufp>=read_bufe) {  // 文件为空
    fprintf(stderr,"Please supply .o5m file at stdin.\n");
return 2;
    }
  if(bufp[0]!=0xff || bufp[1]!=0xe0 || (
      strzcmp((char*)bufp+2,"\x04""o5m2")!=0 &&
      strzcmp((char*)bufp+2,"\x04""o5c2")!=0 )) {
      // not an .o5m format
      fprintf(stderr,"Unknown input file format.\n");
return 3;
    }
  bufp+= 7;  // 跳过.o5m文件头

  // 处理文件
  for(;;) {  //读取输入文件中的所有对象

    // 获取下一个对象
    read_input();
    if(read_bufp>=read_bufe)  // 物理文件结束
  break;
    if(o5endoffile) {  // 在文件的逻辑结束之后
      fprintf(stderr,"Warning: unexpected contents "
        "after logical end of file.\n");
  break;
      }
    bufp= read_bufp;
    b= *bufp;

    // 关心文件头对象和特殊对象
    if(b<0x10 || b>0x12) {  // 不是常规的OSM对象
      if(b>=0xf0) {  // 单字节数据集
        if(b==0xff) {  // 文件开始, resp. o5m 重置
          // 为写入o5m文件重置计数器;
          o5id= 0;
          o5lat= o5lon= 0;
          o5hiscset= 0;
          o5histime= 0;
          o5ref[0]= o5ref[1]= o5ref[2]= 0;
          str_reset();
          }
        else if(b==0xfe) // 文件结束
          o5endoffile= true;
        else if(write_testmode)
          fprintf(stderr,"Unknown .o5m short dataset id: 0x%02x\n",b);
        read_bufp++;
        }  // 单字节数据集
      else {  // 多字节数据集
        bufp++;
        l= pbf_uint32(&bufp);
        bufe= bufp+l;
        if(b==0xdc) {
            // 文件的时间戳
          if(bufp=bufe)
        // 只有id和作者,即这是一个删除请求
      printf("  action: delete\n");
    else {  // not a delete request
      // 读取坐标(仅用于节点)
      if(otype==0) {  // node
        char s[15];

        printf("  lon: %s\n",
          sint32tosfix7(o5lon+= pbf_sint32(&bufp),s));
        printf("  lat: %s\n",
          sint32tosfix7(o5lat+= pbf_sint32(&bufp),s));
        }  // node
      // 读取noderefs(仅用于路线)
      if(otype==1) {  // way
        byte* bp;

        l= pbf_uint32(&bufp); // noderefs 的长度
        bp= bufp+l; // 跳到 noderefs 的结尾
        if(bp>bufe) bp= bufe;  // (format error)
        while(bufpbufe) bp= bufe;  // (format error)
        while(bufp

你可能感兴趣的:(o5mparser)