// 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 <inttypes.h> //提供各种进制的转换宏
#include <stdlib.h> //申请、释放内存空间
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h> // 是POSIX标准定义的unix类系统定义符号常量的头文件
#include <fcntl.h> // 用来避免一些系统安全问题
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()
//------------------------------------------------------------
// Module pbf_ protobuf转换模块
//------------------------------------------------------------
// 这个模块提供了从protobuf格式到常规数字转换的程序;
// 像往常一样,一个模块的所有标识符都有相同的前缀;
// 此时是 'pbf'; 在全局可访问的对象中将有一个下划线
// 在本模块之外不可访问的对象将有两个下划线
// 私有和公共定义的部分用水平线分隔:----
// 许多程序都有一个参数'pp';这里预期会有一个缓冲区指针
// 这个指针会增加被转换的protobuf元素消耗的字节数
//------------------------------------------------------------
static inline uint32_t pbf_uint32(byte** pp) {
// 获取无符号整数的值;
// 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) {
// 获取有符号整数的值;
// 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) {
// 获取无符号整数的值;
// 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) {
// 获取有符号整数的值;
// 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)
// 每次调用read_input()后缓冲区中可用的字节数;
#define read__bufM (read_PREFETCH*5) // 缓冲区的长度;
static bool read__eof; // 输入文件末尾的标识符。
static byte* read__buf = NULL; // 文件输入缓冲区的起始地址
//------------------------------------------------------------
static byte* read_bufp= NULL; // (全局指针)
// 可能会被外部增加
// 在read_input()再次调用之前增加到read_PREFETCH字节数
static byte* read_bufe= NULL; // (全局指针)
// 可能不会从外部改变
static void read__close() {
// 关闭先前打开的输入流的预取缓冲区;
// 将在程序结束时自动调用;
if(read__buf!=NULL) { // buffer is valid
free(read__buf);
read__buf= NULL;
} // buffer is valid
read_bufp= read_bufe= NULL;
} // read__close()
static int read_open() {
// 使用预取缓冲区打开标准输入;
// 返回: 0: 正常; !=0: 错误;
// read__close() 将在程序结束时自动调用;
// 获取文件信息和输入缓冲区的内存空间。
if(read__buf==NULL) { // 缓冲未分配
read__buf= (byte*)malloc(read__bufM); // 分配缓冲区空间
if(read__buf==NULL) {
fprintf(stderr,"Could not get %i bytes of memory.\n",read__bufM); // 分配空间失败
return 1;
}
atexit(read__close); // 结束程序
} // 缓冲未分配
// 初始化读取缓冲区的指针
read__eof= false; // 我们不在输入文件的末尾。
read_bufp= read_bufe= read__buf;
// 指向buf []中有效输入结尾的指针
return 0;
} // read_open()
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__eof) { // 仍然是文件中的字节(文件结尾标志位false)
if(read_bufe>read_bufp) { // 缓冲区中有剩余的字节(当前字节未到达缓冲区结束字节)
memmove(read__buf,read_bufp,read_bufe-read_bufp);
// 移动剩余的字节以开始缓冲区
// (由read_bufp所指内存区域复制read_bufe-read_bufp个字节到read_buf所指内存区域)
read_bufe= read__buf+(read_bufe-read_bufp);
// 在缓冲区开始时保护剩余的字节
}
else // 缓冲区中没有剩余字节
read_bufe= read__buf; // 没有剩余的字节来保护
// 添加读取字节到调试计数器
read_bufp= read__buf;
do { // 缓冲区没有被填满
l= (read__buf+read__bufM)-read_bufe-4;
// 要读取的字节数
r= read(0,read_bufe,l); // 从打开的文件中读取l字节的数据,返回实际读取的字节数
if(r<=0) { // 文件中不再有字节
read__eof= true;
// 记住我们在文件的末尾
l= (read__buf+read__bufM)-read_bufe;
// 缓冲区中有剩余空间
if(l>read_PREFETCH) l= read_PREFETCH;
memset(read_bufe,0,l); // 2011-12-24
// 将缓冲区中直到prefetch的剩余空间设置为0
break;
}
read_bufe+= r; // 为数据的结束设置新标记
read_bufe[0]= 0; read_bufe[1]= 0; // 设置4个空终止符
read_bufe[2]= 0; read_bufe[3]= 0;
} while(r<l); // end 在缓冲区未被填充时结束
} // 仍然读取字节
} // 读缓冲区过低
return read__eof && read_bufp>=read_bufe;
} // 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) { // single string
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__tabn++;
} // 单个字符串对于字符串表来说足够短
} // single string
else { // 字符串对
*s2p= p;
len2= strlen(p);
p+= len2+1;
if(!donotstore && len1+len2<=str__tabstrM) {
// 字符串对 对于字符串表来说足够短
memcpy(str__tab[str__tabi],*s1p,len1+len2+2);
if(++str__tabi>=str__tabM) str__tabi= 0;
if(str__tabn<str__tabM) str__tabn++;
} // 字符串对 对于字符串表来说足够短
} // 字符串对
*pp= (byte*)p;
} // 直接给出的字符串(对)
else { // 通过引用给出的字符串(对)
ref= pbf_uint32(pp);
if(ref>str__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() {
// 开始处理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();
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) {
char ts[25];
uint64totimestamp(pbf_sint64(&bufp),ts);
printf("file timestamp: %s\n",ts);
}
} // 文件的时间戳
else if(b==0xdb) { // border box
char s[15];
if(bufp<bufe) printf("bBox x1=%s\n",
sint32tosfix7(pbf_sint32(&bufp),s));
if(bufp<bufe) printf("bBox y1=%s\n",
sint32tosfix7(pbf_sint32(&bufp),s));
if(bufp<bufe) printf("bBox x2=%s\n",
sint32tosfix7(pbf_sint32(&bufp),s));
if(bufp<bufe) printf("bBox y2=%s\n",
sint32tosfix7(pbf_sint32(&bufp),s));
} // border box
else { // 未知的多字节数据集
if(write_testmode)
fprintf(stderr,"Unknown .o5m dataset id: 0x%02x\n",b);
} // 未知的多字节数据集
read_bufp= bufe;
} // 多字节数据集
continue;
} // 不是常规的OSM对象
// 这里: 常规OSM对象
otype= b&3;
bufp++; // 跳过数据集id
// 读一个osm对象
// 读取对象id
l= pbf_uint32(&bufp);
read_bufp= bufe= bufp+l;
printf("%s: %"PRIi64"\n",ONAME(otype),o5id+= pbf_sint64(&bufp));
/* 读作者 */ {
uint32_t hisver;
int64_t histime;
hisver= pbf_uint32(&bufp);
if(hisver!=0) { // author information available
printf(" version: %"PRIi32"\n",hisver);
histime= o5histime+= pbf_sint64(&bufp);
if(histime!=0) {
char ts[25];
char* hisuid;
char* hisuser;
uint64totimestamp(histime,ts);
printf(" timestamp: %s\n",ts);
printf(" changeset: %"PRIi64"\n",
o5hiscset+= pbf_sint64(&bufp));
str_read(&bufp,&hisuid,&hisuser);
printf(" uid/user: %"PRIu32" %s\n",
(uint32_t)pbf_uint64((byte**)&hisuid),hisuser);
}
} // author information available
} // 读作者
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);
bp= bufp+l;
if(bp>bufe) bp= bufe; // (format error)
while(bufp<bp)
printf(" noderef: %"PRIi64"\n",
o5ref[0]+= pbf_sint64(&bufp));
} // way
// 阅读参考(仅用于关系)
else if(otype==2) { // relation
byte* bp;
int64_t ri; // 暂时的,refid
int rt; // 暂时的, reftype
char* rr; // 暂时的, refrole
l= pbf_uint32(&bufp);
bp= bufp+l;
if(bp>bufe) bp= bufe; // (format error)
while(bufp<bp) {
ri= pbf_sint64(&bufp);
str_read(&bufp,&rr,NULL);
rt= *rr++ & 3; // convert '0'..'2' to 0..2
printf(" ref: %s %"PRIi64" %s\n",
ONAME(rt),o5ref[rt]+= ri,rr);
}
} // relation
// 读取结点的key/val对
while(bufp<bufe) {
char* kp,*vp;
str_read(&bufp,&kp,&vp);
printf(" key/val: %s=%s\n",kp,vp);
}
} // not a delete request
} // 读取输入文件中的所有对象
if(!o5endoffile) // 缺少文件的逻辑结束
fprintf(stderr,"Unexpected end of input file.\n");
return 0;
} // process_o5m()
int main(int argc,char** argv) {
// main procedure;
int r;
#if __WIN32__
setmode(fileno(stdout),O_BINARY);
setmode(fileno(stdin),O_BINARY);
#endif
r= process_o5m();
return r;
} // main()
网友评论