From 4b84fba68b3ba6960e15bec5a31888a44bda9665 Mon Sep 17 00:00:00 2001 From: leitner Date: Fri, 22 Nov 2024 19:40:24 +0000 Subject: [PATCH] more function attribute updates --- buffer.h | 57 ++++++++++------------- buffer/buffer_fromarray.c | 13 ++++-- case.h | 16 ++----- compiler.h | 98 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 136 insertions(+), 48 deletions(-) diff --git a/buffer.h b/buffer.h index d35acc2..ecdd750 100644 --- a/buffer.h +++ b/buffer.h @@ -15,17 +15,6 @@ extern "C" { #endif -/* dietlibc defines these in sys/cdefs.h, which is included from stddef.h */ -#ifndef __writememsz__ -#define __writememsz__(a,b) -#endif -#ifndef __readmemsz__ -#define __readmemsz__(a,b) -#endif -#ifndef __readmem__ -#define __readmem__(a) -#endif - typedef struct buffer { char *x; /* actual buffer space */ size_t p; /* current position */ @@ -45,22 +34,22 @@ typedef struct buffer { /* Initialize a buffer with an existing memory area, which the buffer * will NOT take ownership of (i.e. won't free the memory when it's done) */ -att_writen(4,5) +__bufout(4,5) void buffer_init(buffer* b, ssize_t (*op)(), int fd, char* y, size_t ylen); /* Initialize a buffer with an existing memory area, which the buffer * WILL take ownership of (it will call free() on it when it's done) */ -att_writen(4,5) +__bufout(4,5) void buffer_init_free(buffer* b,ssize_t (*op)(),int fd,char* y,size_t ylen); /* Call buffer_init with op=read(), return 0. * If fd==-1, return -1 instead, leaving b untouched. */ -att_writen(3,4) +__bufout(3,4) int buffer_init_read(buffer* b, int fd, char* y, size_t ylen); /* Call buffer_init with op=write(), return 0. * If fd==-1, return -1 instead, leaving b untouched. */ -att_writen(3,4) +__bufout(3,4) int buffer_init_write(buffer* b, int fd, char* y, size_t ylen); /* Will allocate a buffer of size ylen and then call @@ -83,12 +72,12 @@ int buffer_init_write_allocbuf(buffer* b, int fd, size_t ylen); * that memory area. If it reaches the end, it will signal EOF and never * actually attempt to read from any actual file. * Does not take ownership. Useful for testing. */ -att_readn(2,3) +__bufin(2,3) void buffer_init_staticcontents(buffer* b,char* y,size_t ylen); /* Same but the buffer takes ownership of the static buffer and frees it * in buffer_close. */ -att_readn(2,3) +__bufin(2,3) void buffer_init_staticcontents_free(buffer* b,char* y,size_t ylen); @@ -102,7 +91,7 @@ void buffer_munmap(void* buf); /* Initialize a buffer so it will read from this file by memory mapping * the whole thing. */ -att_read(2) +__strin(2) int buffer_mmapread(buffer* b,const char* filename); /* Indicate you are done with a buffer. @@ -115,22 +104,22 @@ void buffer_close(buffer* b); * the data in the buffer. */ int buffer_flush(buffer* b); -att_readn(2,3) +__bufin(2,3) int buffer_put(buffer* b,const char* x,size_t len); -att_readn(2,3) +__bufin(2,3) int buffer_putalign(buffer* b,const char* x,size_t len); -att_readn(2,3) +__bufin(2,3) int buffer_putflush(buffer* b,const char* x,size_t len); -att_read(2) +__strin(2) int buffer_puts(buffer* b,const char* x); -att_read(2) +__strin(2) int buffer_putsalign(buffer* b,const char* x); -att_read(2) +__strin(2) int buffer_putsflush(buffer* b,const char* x); #if defined(__GNUC__) && !defined(__LIBOWFAT_INTERNAL) @@ -155,22 +144,24 @@ int buffer_putnlflush(buffer* b); /* put \n and flush */ : buffer_put((s),&(c),1) \ ) -att_writen(2,3) +__bufout(2,3) ssize_t buffer_get(buffer* b,char* x,size_t len); ssize_t buffer_feed(buffer* b); +att_write(2) att_nonnull(2) ssize_t buffer_getc(buffer* b,char* x); +att_write(2) att_nonnull(2) ssize_t buffer_peekc(buffer* b,char* x); -att_writen(2,3) +__bufout(2,3) ssize_t buffer_getn(buffer* b,char* x,size_t len); /* read bytes until the destination buffer is full (len bytes), end of * file is reached or the read char is in charset (setlen bytes). An * empty line when looking for \n will write '\n' to x and return 0. If * EOF is reached, \0 is written to the buffer */ -att_writen(2,3) -att_readn(4,5) +__bufout(2,3) +__bufin(4,5) ssize_t buffer_get_token(buffer* b,char* x,size_t len,const char* charset,size_t setlen); -att_writen(2,3) +__bufout(2,3) ssize_t buffer_getline(buffer* b,char* x,size_t len); /* this predicate is given the string as currently read from the buffer @@ -178,7 +169,7 @@ ssize_t buffer_getline(buffer* b,char* x,size_t len); typedef int (*string_predicate)(const char* x,size_t len); /* like buffer_get_token but the token ends when your predicate says so */ -att_writen(2,3) +__bufout(2,3) ssize_t buffer_get_token_pred(buffer* b,char* x,size_t len,string_predicate p); char *buffer_peek(buffer* b); @@ -228,13 +219,13 @@ int buffer_putsaflush(buffer* b,const stralloc* sa); * data is available. */ /* read token from buffer to stralloc */ -att_readn(3,4) +__bufin(3,4) int buffer_get_token_sa(buffer* b,stralloc* sa,const char* charset,size_t setlen); /* read line from buffer to stralloc */ int buffer_getline_sa(buffer* b,stralloc* sa); /* same as buffer_get_token_sa but empty sa first */ -att_readn(3,4) +__bufin(3,4) int buffer_get_new_token_sa(buffer* b,stralloc* sa,const char* charset,size_t setlen); /* same as buffer_getline_sa but empty sa first */ int buffer_getnewline_sa(buffer* b,stralloc* sa); @@ -252,7 +243,7 @@ void buffer_fromsa(buffer* b,const stralloc* sa); /* read from sa */ int buffer_tosa(buffer*b,stralloc* sa); /* write to sa, auto-growing it */ #endif -att_readn(2,3) +__bufin(2,3) void buffer_frombuf(buffer* b,const char* x,size_t l); /* buffer reads from static buffer */ #ifdef ARRAY_H diff --git a/buffer/buffer_fromarray.c b/buffer/buffer_fromarray.c index 45ab9a6..77d8483 100644 --- a/buffer/buffer_fromarray.c +++ b/buffer/buffer_fromarray.c @@ -1,9 +1,16 @@ +#include #include "array.h" #include "buffer.h" +static ssize_t fail() { + errno=EINVAL; + return -1; +} + void buffer_fromarray(buffer* b,array* a) { - if (array_failed(a)) - buffer_frombuf(b,NULL,0); - else + if (array_failed(a)) { + memset(b,0,sizeof *b); + b->op=fail; + } else buffer_frombuf(b,array_start(a),a->initialized); } diff --git a/case.h b/case.h index 471c3af..8275881 100644 --- a/case.h +++ b/case.h @@ -10,29 +10,23 @@ extern "C" { #endif /* turn upper case letters to lower case letters, ASCIIZ */ -att_mangle(1) +__strinout(1) void case_lowers(char *s); /* turn upper case letters to lower case letters, binary */ -att_manglen(1,2) +__strninout(1,2) void case_lowerb(void *buf,size_t len); /* like str_diff, ignoring case */ -att_pure -att_read(1) -att_read(2) +att_pure __strin(1) __strin(2) int case_diffs(const char *,const char *); /* like byte_diff, ignoring case */ -att_pure -att_readn(1,2) -att_readn(3,2) +att_pure __strnin(1,2) __strnin(3,2) int case_diffb(const void *,size_t ,const void *); /* like str_start, ignoring case */ -att_pure -att_read(1) -att_read(2) +att_pure __strin(1) __strin(2) int case_starts(const char *,const char *); #define case_equals(s,t) (!case_diffs((s),(t))) diff --git a/compiler.h b/compiler.h index eb77793..915dcee 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef LIBOWFAT_ATTRIBUTES_H #define LIBOWFAT_ATTRIBUTES_H - +// This is here so you can use inline with older gcc versions #if __GNUC__ && (__STDC_VERSION__ < 199901L) #define inline __inline__ #endif @@ -12,6 +12,7 @@ #define __extension__ #endif +// This is here so we can put noexcept on function declarations in headers #ifndef __cplusplus #define noexcept #endif @@ -24,6 +25,9 @@ #define restrict #endif +// Use this to tell the compiler something is likely to be true or false +// The compiler can use that to give hints to the CPU and group error +// handling code to "cold" pages that never get paged in by the OS #if GCC_VERSION_ATLEAST(3,0) #define likely(a) __builtin_expect((a), 1) #define unlikely(a) __builtin_expect((a), 0) @@ -32,30 +36,49 @@ #define unlikely(a) (a) #endif +// Function attribute for functions like abs() that depend only on the +// arguments and does not access any other memory in the program. The +// compiler can then optimize multiple calls on the same value into just +// one call. #if GCC_VERSION_ATLEAST(2, 5) #define att_const __attribute__((__const__)) #else #define att_const #endif +// Function attribute. Tells the compiler the function has no effect on +// the state of the program except for the return value, but unlike +// att_const the function can read other global state in the program, +// like the memory pointer arguments point to. #if GCC_VERSION_ATLEAST(2, 96) #define att_pure __attribute__((__pure__)) #else #define att_pure #endif +// Function attribute for functions that return freshly allocated +// memory. The compiler then knows the pointer does not alias with other +// pointers, which enables several optimizations. #if GCC_VERSION_ATLEAST(3, 0) #define att_malloc __attribute__((__malloc__)) #else #define att_malloc #endif +// Function attribute for functions that return an important value, like +// an error code, where it would be a security issue not to check the +// return value, for example link or rename. The compiler will then warn +// if the caller ignores the return value. #if GCC_VERSION_ATLEAST(3, 4) #define att_warn_unused_result __attribute__((__warn_unused_result__)) #else #define att_warn_unused_result #endif +// Function attributes. The compiler will put hot and cold functions +// adjacent in memory. If you mark error handling functions as +// cold, the compiler can arrange the binary so the operating system +// never reads those pages into main memory from disk. #if GCC_VERSION_ATLEAST(4, 3) #define att_hot __attribute__((__hot__)) #define att_cold __attribute__((__cold__)) @@ -64,6 +87,13 @@ #define att_cold #endif +// Function attributes. att_alloc tells the compiler that the compiler +// returns a buffer of the size given in the function argument x +// (as an index, counted left to right, starting at 1). att_calloc says +// the buffer size is a*b (both again as an function argument index, +// counted from left to right, starting at 1) +// If we tell the compiler the buffer size, it can detect if we access +// values outside the buffer area. #if GCC_VERSION_ATLEAST(4, 3) #define att_alloc(x) __attribute__((alloc_size(x))) #define att_calloc(x,y) __attribute__((alloc_size(x,y))) @@ -72,6 +102,13 @@ #define att_calloc #endif +// Function attributes. +// att_read(1) says the function reads from the first function argument. +// att_readn(1,2) is like att_read but the buffer size is given as the second argument. +// att_write(1) says the function writes to the first function argument. +// att_writen(1) is like att_write but the buffer size is given as the second argument. +// att_mangle(1) says the function read/writes to the first function argument. +// att_manglen(1) is like att_mangle but the buffer size is given as the second argument. #if GCC_VERSION_ATLEAST(10, 0) #define att_read(argno_ptr) __attribute__((access(read_only, argno_ptr))) #define att_readn(argno_ptr, argno_size) __attribute__((access(read_only, argno_ptr, argno_size))) @@ -88,18 +125,24 @@ #define att_manglen(argno_ptr, argno_size) #endif +// Function attribute. The compiler will warn if you call this function. #if GCC_VERSION_ATLEAST(3, 2) #define att_dontuse __attribute__((__deprecated__)) #else #define att_dontuse #endif +// Function attribute. Tells the compiler that the param's function +// argument can not be NULL (given as index of function arguments, +// counted left to right from 1) #if GCC_VERSION_ATLEAST(3, 3) #define att_nonnull(params) __attribute__((__nonnull__(params))) #else #define att_nonnull(params) #endif +// Function attribute. Makes the compiler emit a warning or an error, +// abort compilation on error. #if GCC_VERSION_ATLEAST(4, 3) #define att_warn(message) __attribute__((__warning__(message))) #define att_error(message) __attribute__((__warning__(message))) @@ -108,8 +151,61 @@ #define att_error(message) #endif +// __builtin_constant_p is a GCC extension used in macros to handle +// cases differently where a macro argument is a constant #ifndef __GNUC__ #define __builtin_constant_p(x) 0 #endif +#if defined(__GNUC__) +#if __has_attribute(null_terminated_string_arg) +// Function arguments. +// __strin: Argument idx (counted left to right from 1) is a non-NULL string the function reads +#define __strin(idx) __attribute__((nonnull(idx),access(read_only,idx),null_terminated_string_arg(idx))) +// __strnin: Argument idx (counted left to right from 1) is a non-NULL string the function reads, length limit in array elements given in the szidx'th argument +#define __strnin(idx,szidx) __attribute__((nonnull(idx),access(read_only,idx,szidx),null_terminated_string_arg(idx))) +// __bufin: Argument idx (counted left to right from 1) is a non-NULL buffer the function reads from, length limit in array elements given in the szidx'th argument +#define __bufin(bufidx,szidx) __attribute__((nonnull(bufidx),access(read_only,bufidx,szidx))) +// __strout: Argument idx (counted left to right from 1) is a non-NULL string the function writes +// Currently we can't tell the compiler that the output will be 0-terminated. +#define __strout(idx) __attribute__((nonnull(idx),access(write_only,idx))) +// __strnout: Argument idx (counted left to right from 1) is a non-NULL string the function writes, buffer size given in szidx'th function argument (in array elements, not necessarily bytes) +// Currently we can't tell the compiler that the output will be 0-terminated. +#define __strnout(idx,szidx) __attribute__((nonnull(idx),access(write_only,idx,szidx))) +// __bufout: Argument idx (counted left to right from 1) is a non-NULL buffer the function writes, buffer size given in szidx'th function argument (in array elements, not necessarily bytes) +#define __bufout(bufidx,szidx) __attribute__((nonnull(bufidx),access(write_only,bufidx,szidx))) +// __strinout: Argument idx (counted left to right from 1) is a non-NULL string the function reads and writes +// Currently we can't tell the compiler that the output will be 0-terminated. +#define __strinout(idx) __attribute__((nonnull(idx),access(read_write,idx),null_terminated_string_arg(idx))) +// __strninout: Argument idx (counted left to right from 1) is a non-NULL string the function reads and writes, buffer size in szidx'th function argument (in array elements, not necessarily bytes) +// Currently we can't tell the compiler that the output will be 0-terminated. +#define __strninout(idx,szidx) __attribute__((nonnull(idx),access(read_write,idx,szidx),null_terminated_string_arg(idx))) +// __bufinout: Argument idx (counted left to right from 1) is a non-NULL buffer the function reads and writes, buffer size in szidx'th function argument (in array elements, not necessarily bytes) +#define __bufinout(bufidx,szidx) __attribute__((nonnull(bufidx),access(read_write,bufidx,szidx))) +#elif GCC_VERSION_ATLEAST(10, 0) +#define __strin(idx) __attribute__((nonnull(idx),access(read_only,idx))) +#define __strnin(idx,szidx) __attribute__((nonnull(idx),access(read_only,idx,szidx))) +#define __bufin(bufidx,szidx) __attribute__((nonnull(bufidx),access(read_only,bufidx,szidx))) +#define __strout(idx) __attribute__((nonnull(idx),access(write_only,idx))) +#define __strnout(idx,szidx) __attribute__((nonnull(idx),access(write_only,idx,szidx))) +#define __bufout(bufidx,szidx) __attribute__((nonnull(bufidx),access(write_only,bufidx,szidx))) +#define __strinout(idx) __attribute__((nonnull(idx),access(read_write,idx))) +#define __strninout(idx,szidx) __attribute__((nonnull(idx),access(read_write,idx,szidx))) +#define __bufinout(bufidx,szidx) __attribute__((nonnull(bufidx),access(read_write,bufidx,szidx))) + +#elif GCC_VERSION_ATLEAST(3, 3) + +#define __strin(idx) __attribute__((nonnull(idx))) +#define __strnin(idx,szidx) __attribute__((nonnull(idx))) +#define __bufin(bufidx,szidx) __attribute__((nonnull(bufidx))) +#define __strout(idx) __attribute__((nonnull(idx))) +#define __strnout(idx,szidx) __attribute__((nonnull(idx))) +#define __bufout(bufidx,szidx) __attribute__((nonnull(bufidx))) +#define __strinout(idx) __attribute__((nonnull(idx))) +#define __strninout(idx,szidx) __attribute__((nonnull(idx))) +#define __bufinout(bufidx,szidx) __attribute__((nonnull(bufidx))) + +#endif +#endif // !defined(__GNUC__) + #endif