diff --git a/fmt.h b/fmt.h index 6dbda3d..9261829 100644 --- a/fmt.h +++ b/fmt.h @@ -10,13 +10,15 @@ #include /* for byte_copy */ #include "byte.h" -/* for add_of */ -#include "rangecheck.h" #ifdef __cplusplus extern "C" { #endif +#ifndef __pure__ +#define __pure__ +#endif + #define FMT_LONG 41 /* enough space to hold -2^127 in decimal, plus \0 */ #define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */ #define FMT_8LONG 44 /* enough space to hold 2^128 - 1 in octal, plus \0 */ @@ -44,31 +46,31 @@ extern "C" { /* convert signed src integer -23 to ASCII '-','2','3', return number of * bytes of value in output format (3 in this example). * If dest is not NULL, write result to dest */ -size_t fmt_long(char *dest,signed long src); +size_t fmt_long(char *dest,signed long src) __pure__; /* convert unsigned src integer 23 to ASCII '2','3', return number of * bytes of value in output format (2 in this example). * If dest is not NULL, write result to dest */ -size_t fmt_ulong(char *dest,unsigned long src); +size_t fmt_ulong(char *dest,unsigned long src) __pure__; /* convert unsigned src integer 0x23 to ASCII '2','3', return number of * bytes of value in output format (2 in this example). * If dest is not NULL, write result to dest */ -size_t fmt_xlong(char *dest,unsigned long src); +size_t fmt_xlong(char *dest,unsigned long src) __pure__; /* convert unsigned src integer 023 to ASCII '2','3', return number of * bytes of value in output format (2 in this example). * If dest is not NULL, write result to dest */ -size_t fmt_8long(char *dest,unsigned long src); +size_t fmt_8long(char *dest,unsigned long src) __pure__; /* like fmt_long but for long long */ -size_t fmt_longlong(char *dest,signed long long src); +size_t fmt_longlong(char *dest,signed long long src) __pure__; /* like fmt_ulong but for unsigned long long */ -size_t fmt_ulonglong(char *dest,unsigned long long src); +size_t fmt_ulonglong(char *dest,unsigned long long src) __pure__; /* like fmt_xlong but for unsigned long long */ -size_t fmt_xlonglong(char *dest,unsigned long long src); +size_t fmt_xlonglong(char *dest,unsigned long long src) __pure__; #define fmt_uint(dest,src) fmt_ulong(dest,src) #define fmt_int(dest,src) fmt_long(dest,src) @@ -79,22 +81,22 @@ size_t fmt_xlonglong(char *dest,unsigned long long src); * Does not truncate! */ /* fmt_ulong0(buf,23,4) -> '0','0','2','3' return 4 */ /* fmt_ulong0(buf,234,2) -> '2','3','4', return 3 */ -size_t fmt_ulong0(char *,unsigned long src,size_t padto); +size_t fmt_ulong0(char *,unsigned long src,size_t padto) __pure__; #define fmt_uint0(buf,src,padto) fmt_ulong0(buf,src,padto) /* convert src double 1.7 to ASCII '1','.','7', return length. * If dest is not NULL, write result to dest */ -size_t fmt_double(char *dest, double d,int max,int prec); +size_t fmt_double(char *dest, double d,int max,int prec) __pure__; /* if src is negative, write '-' and return 1. * if src is positive, write '+' and return 1. * otherwise return 0 */ -size_t fmt_plusminus(char *dest,int src); +size_t fmt_plusminus(char *dest,int src) __pure__; /* if src is negative, write '-' and return 1. * otherwise return 0. */ -size_t fmt_minus(char *dest,int src); +size_t fmt_minus(char *dest,int src) __pure__; /* copy str to dest until \0 byte, return number of copied bytes. */ /* fmt_str(NULL,str) == strlen(str) */ @@ -106,11 +108,11 @@ size_t fmt_minus(char *dest,int src); * This is more efficient because strcat needs to scan the string to * find the end and append. */ -size_t fmt_str(char *dest,const char *src); +size_t fmt_str(char *dest,const char *src) __pure__; /* copy str to dest until \0 byte or limit bytes copied. * return number of copied bytes. */ -size_t fmt_strn(char *dest,const char *src,size_t limit); +size_t fmt_strn(char *dest,const char *src,size_t limit) __pure__; /* copy n bytes from src to dest, return n */ static inline size_t fmt_copybytes(char* dest,const char* src,size_t n) { @@ -122,66 +124,56 @@ static inline size_t fmt_copybytes(char* dest,const char* src,size_t n) { * write padlen-srclen spaces, if that is >= 0. Then copy srclen * characters from src. Truncate only if total length is larger than * maxlen. Return number of characters written. */ -size_t fmt_pad(char* dest,const char* src,size_t srclen,size_t padlen,size_t maxlen); +size_t fmt_pad(char* dest,const char* src,size_t srclen,size_t padlen,size_t maxlen) __pure__; /* "foo" -> "foo " * append padlen-srclen spaces after dest, if that is >= 0. Truncate * only if total length is larger than maxlen. Return number of * characters written. */ -size_t fmt_fill(char* dest,size_t srclen,size_t padlen,size_t maxlen); +size_t fmt_fill(char* dest,size_t srclen,size_t padlen,size_t maxlen) __pure__; /* 1 -> "1", 4900 -> "4.9k", 2300000 -> "2.3M" */ -size_t fmt_human(char* dest,unsigned long long l); +size_t fmt_human(char* dest,unsigned long long l) __pure__; /* 1 -> "1", 4900 -> "4.8k", 2300000 -> "2.2M" */ -size_t fmt_humank(char* dest,unsigned long long l); +size_t fmt_humank(char* dest,unsigned long long l) __pure__; /* "Sun, 06 Nov 1994 08:49:37 GMT" */ -size_t fmt_httpdate(char* dest,time_t t); +size_t fmt_httpdate(char* dest,time_t t); /* not marked pure because it calls gmtime */ /* "2014-05-27T19:22:16.247Z" */ -size_t fmt_iso8601(char* dest,time_t t); +size_t fmt_iso8601(char* dest,time_t t) __pure__; #define FMT_UTF8 5 #define FMT_ASN1LENGTH 17 /* enough space to hold 2^128-1 */ #define FMT_ASN1TAG 19 /* enough space to hold 2^128-1 */ /* some variable length encodings for integers */ -size_t fmt_utf8(char* dest,uint32_t n); /* can store 0-0x7fffffff */ -size_t fmt_asn1derlength(char* dest,unsigned long long l); /* 0-0x7f: 1 byte, above that 1+bytes_needed bytes */ -size_t fmt_asn1dertag(char* dest,unsigned long long l); /* 1 byte for each 7 bits; upper bit = more bytes coming */ -size_t fmt_varint(char* dest,unsigned long long l); /* protocol buffers encoding; like asn1dertag but little endian */ -size_t fmt_pb_tag(char* dest,size_t fieldno,unsigned char type); /* protocol buffer tag */ -size_t fmt_pb_type0_int(char* dest,signed long long l); /* protocol buffers encoding: type 0 integer */ -size_t fmt_pb_type1_double(char* dest,double d); /* protocol buffers encoding: double (64-bit little endian blob) */ -size_t fmt_pb_type1_fixed64(char* dest,uint64_t l); /* protocol buffers encoding: 64-bit little endian blob */ -size_t fmt_pb_type2_string(char* dest,const char* s,size_t l); /* protocol buffers encoding: varint length + blob */ -size_t fmt_pb_type5_float(char* dest,float f); /* protocol buffers encoding: float (32-bit little endian blob) */ -size_t fmt_pb_type5_fixed32(char* dest,uint32_t l); /* protocol buffers encoding: 32-bit little endian blob */ - -static inline size_t fmt_pb_int(char* dest,size_t fieldno,signed long long l) { - size_t n=fmt_pb_tag(dest,fieldno,0); - return n+fmt_pb_type0_int(dest?dest+n:0,l); -} - -static inline size_t fmt_pb_double(char* dest,size_t fieldno,double d) { - size_t n=fmt_pb_tag(dest,fieldno,1); - return n+fmt_pb_type1_double(dest?dest+n:0,d); -} - -static inline size_t fmt_pb_float(char* dest,size_t fieldno,float f) { - size_t n=fmt_pb_tag(dest,fieldno,5); - return n+fmt_pb_type5_float(dest?dest+n:0,f); -} - -static inline size_t fmt_pb_string(char* dest,size_t fieldno,const char* s,size_t l) { - size_t n=fmt_pb_tag(dest,fieldno,2); - size_t m; - if (add_of(m,fmt_pb_type2_string(NULL,s,l),n)) return 0; - return n+fmt_pb_type2_string(dest?dest+n:0,s,l); -} - -size_t fmt_netstring(char* dest,const char* src,size_t len); +size_t fmt_utf8(char* dest,uint32_t n) __pure__; /* can store 0-0x7fffffff */ +size_t fmt_asn1derlength(char* dest,unsigned long long l) __pure__; /* 0-0x7f: 1 byte, above that 1+bytes_needed bytes */ +size_t fmt_asn1dertag(char* dest,unsigned long long l) __pure__; /* 1 byte for each 7 bits; upper bit = more bytes coming */ + +/* Google Protocol Buffers, https://developers.google.com/protocol-buffers/docs/encoding */ +size_t fmt_varint(char* dest,unsigned long long l) __pure__; /* protocol buffers encoding; like asn1dertag but little endian */ +size_t fmt_pb_tag(char* dest,size_t fieldno,unsigned char type) __pure__; /* protocol buffer tag */ +size_t fmt_pb_type0_int(char* dest,unsigned long long l) __pure__; /* protocol buffers encoding: type 0 bool/enum/int32/uint32/int64/uint64 */ +size_t fmt_pb_type0_sint(char* dest,signed long long l) __pure__;/* protocol buffers encoding: type 0 sint32/sint64 */ +size_t fmt_pb_type1_double(char* dest,double d) __pure__; /* protocol buffers encoding: double (64-bit little endian blob) */ +size_t fmt_pb_type1_fixed64(char* dest,uint64_t l) __pure__; /* protocol buffers encoding: 64-bit little endian blob */ + +/* fmt_pb_type2_string can return 0 if (s,l) is clearly invalid */ +size_t fmt_pb_type2_string(char* dest,const char* s,size_t l) __pure__; /* protocol buffers encoding: varint length + blob */ +size_t fmt_pb_type5_float(char* dest,float f) __pure__; /* protocol buffers encoding: float (32-bit little endian blob) */ +size_t fmt_pb_type5_fixed32(char* dest,uint32_t l) __pure__; /* protocol buffers encoding: 32-bit little endian blob */ + +size_t fmt_pb_int(char* dest,size_t fieldno,unsigned long long l) __pure__; +size_t fmt_pb_sint(char* dest,size_t fieldno,signed long long l) __pure__; +size_t fmt_pb_double(char* dest,size_t fieldno,double d) __pure__; +size_t fmt_pb_float(char* dest,size_t fieldno,float f) __pure__; +size_t fmt_pb_string(char* dest,size_t fieldno,const char* s,size_t l) __pure__; + +/* fmt_netstring can return 0 if (src,len) is clearly invalid */ +size_t fmt_netstring(char* dest,const char* src,size_t len) __pure__; /* Marshaling helper functions. * Escape one character, no matter if it needs escaping or not. @@ -193,27 +185,27 @@ size_t fmt_netstring(char* dest,const char* src,size_t len); * unicode codepoint) may be limited to 0x7f, 0xff or 0x10ffff. */ /* XML escaping: '&' -> '&', '<' -> '<', 'ö' -> 'ö' */ -size_t fmt_escapecharxml(char* dest,uint32_t ch); +size_t fmt_escapecharxml(char* dest,uint32_t ch) __pure__; /* HTML escaping is the same as XML escaping. */ -size_t fmt_escapecharhtml(char* dest,uint32_t ch); +size_t fmt_escapecharhtml(char* dest,uint32_t ch) __pure__; /* JSON escaping: '\' -> '\\', '"' -> '\"', 'ö' -> '\u00f6' */ -size_t fmt_escapecharjson(char* dest,uint32_t ch); +size_t fmt_escapecharjson(char* dest,uint32_t ch) __pure__; /* MIME quoted-printable escaping: 'ö' -> '=f6', characters > 0xff not supported */ -size_t fmt_escapecharquotedprintable(char* dest,uint32_t ch); +size_t fmt_escapecharquotedprintable(char* dest,uint32_t ch) __pure__; /* MIME quoted-printable escaping with UTF-8: 'ö' -> '=c3=b6', characters > 0x7fffffff not supported */ -size_t fmt_escapecharquotedprintableutf8(char* dest,uint32_t ch); +size_t fmt_escapecharquotedprintableutf8(char* dest,uint32_t ch) __pure__; /* C escaping: '\' -> '\\', newline -> '\n', 0xc2 -> '\302' */ -size_t fmt_escapecharc(char* dest,uint32_t ch); +size_t fmt_escapecharc(char* dest,uint32_t ch) __pure__; /* internal functions, may be independently useful */ -char fmt_tohex(char c); +char fmt_tohex(char c) __attribute__((__const__)); #define fmt_strm(b,...) fmt_strm_internal(b,__VA_ARGS__,(char*)0) -size_t fmt_strm_internal(char* dest,...); +size_t fmt_strm_internal(char* dest,...) __pure__; #ifndef MAX_ALLOCA #define MAX_ALLOCA 100000 diff --git a/fmt/fmt_pb_double.c b/fmt/fmt_pb_double.c new file mode 100644 index 0000000..50969a2 --- /dev/null +++ b/fmt/fmt_pb_double.c @@ -0,0 +1,7 @@ +#include "fmt.h" + +size_t fmt_pb_double(char* dest,size_t fieldno,double d) { + size_t n=fmt_pb_tag(dest,fieldno,1); + return n+fmt_pb_type1_double(dest?dest+n:0,d); +} + diff --git a/fmt/fmt_pb_float.c b/fmt/fmt_pb_float.c new file mode 100644 index 0000000..0812313 --- /dev/null +++ b/fmt/fmt_pb_float.c @@ -0,0 +1,6 @@ +#include "fmt.h" + +size_t fmt_pb_float(char* dest,size_t fieldno,float f) { + size_t n=fmt_pb_tag(dest,fieldno,5); + return n+fmt_pb_type5_float(dest?dest+n:0,f); +} diff --git a/fmt/fmt_pb_int.c b/fmt/fmt_pb_int.c new file mode 100644 index 0000000..76296ab --- /dev/null +++ b/fmt/fmt_pb_int.c @@ -0,0 +1,7 @@ +#include "fmt.h" + +size_t fmt_pb_int(char* dest,size_t fieldno,unsigned long long l) { + size_t n=fmt_pb_tag(dest,fieldno,0); + return n+fmt_pb_type0_int(dest?dest+n:0,l); +} + diff --git a/fmt/fmt_pb_sint.c b/fmt/fmt_pb_sint.c new file mode 100644 index 0000000..2cb13bd --- /dev/null +++ b/fmt/fmt_pb_sint.c @@ -0,0 +1,6 @@ +#include "fmt.h" + +size_t fmt_pb_sint(char* dest,size_t fieldno,signed long long l) { + size_t n=fmt_pb_tag(dest,fieldno,0); + return n+fmt_pb_type0_sint(dest?dest+n:0,l); +} diff --git a/fmt/fmt_pb_string.c b/fmt/fmt_pb_string.c new file mode 100644 index 0000000..4bcb066 --- /dev/null +++ b/fmt/fmt_pb_string.c @@ -0,0 +1,9 @@ +#include "fmt.h" +#include "rangecheck.h" + +size_t fmt_pb_string(char* dest,size_t fieldno,const char* s,size_t l) { + size_t n=fmt_pb_tag(dest,fieldno,2); + size_t m; + if (add_of(m,fmt_pb_type2_string(NULL,s,l),n)) return 0; + return n+fmt_pb_type2_string(dest?dest+n:0,s,l); +} diff --git a/fmt/fmt_pb_tag.c b/fmt/fmt_pb_tag.c new file mode 100644 index 0000000..4b39bba --- /dev/null +++ b/fmt/fmt_pb_tag.c @@ -0,0 +1,6 @@ +#include "fmt.h" + +size_t fmt_pb_tag(char* dest,size_t fieldno,unsigned char type) { + if (type>5 || (fieldno >> (sizeof(fieldno)*8-3))) return 0; + return fmt_varint(dest,(type&7) | (fieldno<<3)); +} diff --git a/fmt/fmt_pb_type0_sint.c b/fmt/fmt_pb_type0_sint.c new file mode 100644 index 0000000..41353c0 --- /dev/null +++ b/fmt/fmt_pb_type0_sint.c @@ -0,0 +1,5 @@ +#include "fmt.h" + +size_t fmt_pb_type0_sint(char* dest,signed long long l) { + return fmt_varint(dest,(l << 1) ^ (l >> (sizeof(l)*8-1))); +} diff --git a/fmt/fmt_pb_type1_double.c b/fmt/fmt_pb_type1_double.c new file mode 100644 index 0000000..f29b259 --- /dev/null +++ b/fmt/fmt_pb_type1_double.c @@ -0,0 +1,12 @@ +#include "fmt.h" +#include "compiletimeassert.h" + +size_t fmt_pb_type1_double(char* dest,double d) { + union { + double d; + uint64_t u; + } u; + compiletimeassert(sizeof(double) == 8); + u.d=d; + return fmt_pb_type1_fixed64(dest,u.u); +} diff --git a/fmt/fmt_pb_type1_fixed64.c b/fmt/fmt_pb_type1_fixed64.c new file mode 100644 index 0000000..67aaf36 --- /dev/null +++ b/fmt/fmt_pb_type1_fixed64.c @@ -0,0 +1,7 @@ +#include "fmt.h" +#include "uint64.h" + +size_t fmt_pb_type1_fixed64(char* dest,uint64_t l) { + if (dest) uint64_pack(dest,l); + return 8; +} diff --git a/fmt/fmt_pb_type2_string.c b/fmt/fmt_pb_type2_string.c new file mode 100644 index 0000000..aa6b516 --- /dev/null +++ b/fmt/fmt_pb_type2_string.c @@ -0,0 +1,10 @@ +#include "fmt.h" +#include "rangecheck.h" +#include + +size_t fmt_pb_type2_string(char* dest,const char* s,size_t l) { + size_t n=fmt_varint(dest,l); + if (add_of(n,l,n)) return 0; + if (dest) memcpy(dest+n-l,s,l); + return n; +} diff --git a/fmt/fmt_pb_type5_fixed32.c b/fmt/fmt_pb_type5_fixed32.c new file mode 100644 index 0000000..70d7c5b --- /dev/null +++ b/fmt/fmt_pb_type5_fixed32.c @@ -0,0 +1,7 @@ +#include "fmt.h" +#include "uint32.h" + +size_t fmt_pb_type5_fixed32(char* dest,uint32_t l) { + if (dest) uint32_pack(dest,l); + return 4; +} diff --git a/fmt/fmt_pb_type5_float.c b/fmt/fmt_pb_type5_float.c new file mode 100644 index 0000000..408bfd2 --- /dev/null +++ b/fmt/fmt_pb_type5_float.c @@ -0,0 +1,12 @@ +#include "fmt.h" +#include "compiletimeassert.h" + +size_t fmt_pb_type5_float(char* dest,float f) { + union { + float f; + uint32_t u; + } u; + compiletimeassert(sizeof(float) == 4); + u.f=f; + return fmt_pb_type5_fixed32(dest,u.u); +} diff --git a/fmt/fmt_varint.c b/fmt/fmt_varint.c new file mode 100644 index 0000000..ae64b29 --- /dev/null +++ b/fmt/fmt_varint.c @@ -0,0 +1,14 @@ +#include "fmt.h" + +/* write int in least amount of bytes, return number of bytes */ +/* as used in varints from Google protocol buffers */ +size_t fmt_varint(char* dest,unsigned long long l) { + /* high bit says if more bytes are coming, lower 7 bits are payload; little endian */ + size_t i; + for (i=0; l; ++i, l>>=7) { + if (dest) dest[i]=(l&0x7f) | ((!!(l&~0x7f))<<7); + } + return i; +} + +size_t fmt_pb_type0_int(char* dest,unsigned long long l) __attribute__((alias("fmt_varint"))); diff --git a/scan.h b/scan.h index f366178..13becb9 100644 --- a/scan.h +++ b/scan.h @@ -114,6 +114,36 @@ size_t scan_utf8(const char* in,size_t len,uint32_t* n) __pure__; size_t scan_asn1derlength(const char* in,size_t len,unsigned long long* n) __pure__; size_t scan_asn1dertag(const char* in,size_t len,unsigned long long* n) __pure__; +/* Google protocol buffers */ +/* A protocol buffer is a sequence of (tag,value). + * Parse each tag with scan_pb_tag, then look at the field number to see + * which field in your struct is being sent. Integers must have type + * 0, double type 1, strings type 2 and floats type 5. However, you + * have to check this yourself. + */ +size_t scan_varint(const char* in,size_t len, unsigned long long* n) __pure__; /* internal */ +size_t scan_pb_tag(const char* in,size_t len, size_t* fieldno,unsigned char* type) __pure__; + +/* Then, depending on the field number, validate the type and call the + * corresponding of these functions to parse the value */ +size_t scan_pb_type0_int(const char* in,size_t len,unsigned long long* l) __pure__; +size_t scan_pb_type0_sint(const char* in,size_t len,signed long long* l) __pure__; +size_t scan_pb_type1_double(const char* in,size_t len,double* d) __pure__; +size_t scan_pb_type1_fixed64(const char* in,size_t len,uint64_t* b) __pure__; +/* NOTE: scan_pb_type2_stringlen only parses the length of the string, + * not the string itself. It will return the number of bytes parsed in + * the length, then set slen to the value of the length integer it just + * read, and let string point to the next byte (where the actual string + * starts). To advance in the protocol buffer, you'll have to skip the + * return value of this function + slen bytes. + * This is done so you can detect too large strings and abort the + * parsing early without having to read and allocate memory for the rest + * (potentially gigabytes) of the data announced by one unreasonable + * string length value. */ +size_t scan_pb_type2_stringlen(const char* in,size_t len,const char** string, size_t* slen) __pure__; +size_t scan_pb_type5_float(const char* in,size_t len,float* f) __pure__; +size_t scan_pb_type5_fixed32(const char* in,size_t len,uint32_t* b) __pure__; + /* parse a netstring, input buffer is in (len bytes). * if parsing is successful: * *dest points to string and *slen is size of string diff --git a/scan/scan_pb_tag.c b/scan/scan_pb_tag.c new file mode 100644 index 0000000..d4b4f5e --- /dev/null +++ b/scan/scan_pb_tag.c @@ -0,0 +1,10 @@ +#include "scan.h" + +size_t scan_pb_tag(const char* in,size_t len, size_t* fieldno,unsigned char* type) { + unsigned long long l; + size_t n=scan_varint(in,len,&l); + if (n==0) return 0; + *type=l&7; + *fieldno=(l>>3); + return n; +} diff --git a/scan/scan_pb_type0_sint.c b/scan/scan_pb_type0_sint.c new file mode 100644 index 0000000..07fe9f1 --- /dev/null +++ b/scan/scan_pb_type0_sint.c @@ -0,0 +1,9 @@ +#include "scan.h" + +size_t scan_pb_type0_sint(const char* in,size_t len,signed long long* l) { + unsigned long long m; + size_t n=scan_varint(in,len,&m); + if (!n) return 0; + *l=(-(m&1)) ^ (m>>1); + return n; +} diff --git a/scan/scan_pb_type1_double.c b/scan/scan_pb_type1_double.c new file mode 100644 index 0000000..2740f24 --- /dev/null +++ b/scan/scan_pb_type1_double.c @@ -0,0 +1,13 @@ +#include "scan.h" +#include "compiletimeassert.h" + +size_t scan_pb_type1_double(const char* in,size_t len,double* d) { + union { + double d; + uint64_t u; + } u; + compiletimeassert(sizeof(double)==8); + size_t n=scan_pb_type1_fixed64(in,len,&u.u); + if (n) *d=u.d; + return n; +} diff --git a/scan/scan_pb_type1_fixed64.c b/scan/scan_pb_type1_fixed64.c new file mode 100644 index 0000000..86fce5d --- /dev/null +++ b/scan/scan_pb_type1_fixed64.c @@ -0,0 +1,8 @@ +#include "scan.h" +#include "uint64.h" + +size_t scan_pb_type1_fixed64(const char* in,size_t len,uint64_t* d) { + if (len<8) return 0; + uint64_unpack(in,d); + return 8; +} diff --git a/scan/scan_pb_type2_stringlen.c b/scan/scan_pb_type2_stringlen.c new file mode 100644 index 0000000..9224f55 --- /dev/null +++ b/scan/scan_pb_type2_stringlen.c @@ -0,0 +1,11 @@ +#include "scan.h" +#include "rangecheck.h" + +size_t scan_pb_type2_stringlen(const char* in,size_t len,const char** string, size_t* slen) { + unsigned long long l; + size_t n=scan_varint(in,len,&l); + if (n && !assign(*slen,l)) { + *string=in+n; + } + return n; +} diff --git a/scan/scan_pb_type5_fixed32.c b/scan/scan_pb_type5_fixed32.c new file mode 100644 index 0000000..9516311 --- /dev/null +++ b/scan/scan_pb_type5_fixed32.c @@ -0,0 +1,8 @@ +#include "scan.h" +#include "uint32.h" + +size_t scan_pb_type5_fixed32(const char* in,size_t len,uint32_t* d) { + if (len<4) return 0; + uint32_unpack(in,d); + return 4; +} diff --git a/scan/scan_pb_type5_float.c b/scan/scan_pb_type5_float.c new file mode 100644 index 0000000..7630423 --- /dev/null +++ b/scan/scan_pb_type5_float.c @@ -0,0 +1,13 @@ +#include "scan.h" +#include "compiletimeassert.h" + +size_t scan_pb_type5_float(const char* in,size_t len,float* f) { + union { + float f; + uint32_t u; + } u; + compiletimeassert(sizeof(float)==4); + size_t n=scan_pb_type5_fixed32(in,len,&u.u); + if (n) *f=u.f; + return n; +} diff --git a/scan/scan_varint.c b/scan/scan_varint.c new file mode 100644 index 0000000..8c6a7ff --- /dev/null +++ b/scan/scan_varint.c @@ -0,0 +1,18 @@ +#include "scan.h" + +size_t scan_varint(const char* in,size_t len, unsigned long long* n) { + size_t i; + unsigned long long l; + if (len==0) return 0; + l=0; + for (l=0, i=0; i +#include +#include +#include + +int main() { + char buf[100]; + double pi=M_PI; + float fpi=pi; + + unsigned long long l; + size_t len; + unsigned char c; + + assert(fmt_varint(buf,23)==1 && buf[0]==23); + assert(scan_varint(buf,1,&l)==1 && l==23); + assert(fmt_varint(buf,150)==2 && buf[0]==(char)0x96 && buf[1]==1); + assert(scan_varint(buf,2,&l)==2 && l==150); + + assert(fmt_pb_tag(buf,23,2)==2 && (unsigned char)buf[0]==((23<<3)+2)); + assert(scan_pb_tag(buf,2,&len,&c)==2 && len==23 && c==2); + + assert(fmt_pb_type0_int(buf,150)==2 && scan_pb_type0_int(buf,2,&l)==2 && l==150); + assert(fmt_pb_type0_sint(buf,150)==2 && scan_pb_type0_sint(buf,2,&l)==2 && l==150); + + { + double d; + assert(fmt_pb_type1_double(buf,pi)==8 && scan_pb_type1_double(buf,8,&d)==8 && d==pi); + } + + { + float f; + assert(fmt_pb_type5_float(buf,fpi)==4 && scan_pb_type5_float(buf,4,&f)==4 && f==fpi); + } + + { + const char* s; + size_t l; + assert(fmt_pb_type2_string(buf,"fnord",5)==6); + assert(scan_pb_type2_stringlen(buf,6,&s,&l)==1 && l==5); + } + + assert(fmt_pb_int(buf,1,150)==3 && !memcmp(buf,"\x08\x96\x01",3)); + assert(fmt_pb_sint(buf,2,150)==3 && !memcmp(buf,"\x10\xac\x02",3)); + assert(fmt_pb_double(buf,3,pi)==9 && buf[0]==0x19); + assert(fmt_pb_float(buf,4,fpi)==5 && buf[0]==0x25); + assert(fmt_pb_string(buf,5,"fnord",5)==7 && !memcmp(buf,"\x2a\x05""fnord",7)); +}