From: Mark Wooding Date: Tue, 12 Jun 2018 00:39:14 +0000 (+0100) Subject: Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/mLib X-Git-Tag: 2.3.1~1 X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/commitdiff_plain/abcb5d8720e264d76f9db2db0497de21fa832602?hp=bf4953f56b1302bf7b9a1ca8b2c0cfe012377674 Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/mLib * 'master' of git.distorted.org.uk:~mdw/publish/public-git/mLib: Release 2.2.5. sel/ident.c: Don't close the descriptor twice on immediate connect failure. --- diff --git a/.skelrc b/.skelrc index 28bf5de..adff5ec 100644 --- a/.skelrc +++ b/.skelrc @@ -5,5 +5,5 @@ '((author . "Straylight/Edgeware") (full-title . "the mLib utilities library") (library . "mLib") - (licence-text . skelrc-lgpl)) + (licence-text . "[[lgpl-2]]")) skel-alist)) diff --git a/codec/url.c b/codec/url.c index f502f7c..3c1fbfd 100644 --- a/codec/url.c +++ b/codec/url.c @@ -63,32 +63,19 @@ static void encode(url_ectx *ctx, dstr *d, const char *p) { while (*p) { switch (*p) { - case ' ': - DPUTC(d, '+'); + case ' ': DPUTC(d, '+'); break; default: - if ((ctx->f & URLF_LAX) || isalnum((unsigned char)*p)) - goto safe; - else - goto unsafe; - case '/': - case '~': - if (ctx->f & URLF_STRICT) - goto unsafe; - case '-': - case '.': - case '_': - safe: - DPUTC(d, *p); - break; - unsafe: - case '+': - case '%': - case '=': - case '&': - case ';': - dstr_putf(d, "%%%02x", *p); - break; + if (isspace((unsigned char)*p)) goto unsafe; + else if (isalnum((unsigned char *)p)) goto safe; + else if (ctx->f&URLF_LAX) goto safe; + else goto unsafe; + case '/': case '~': + if (ctx->f&URLF_STRICT) goto unsafe; /* else fall through... */ + safe: case '-': case '.': case '_': + DPUTC(d, *p); break; + unsafe: case '+': case '%': case '=': case '&': case ';': + dstr_putf(d, "%%%02x", *p); break; } p++; } diff --git a/debian/changelog b/debian/changelog index 0794729..c815542 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +mlib (2.3.0) experimental; urgency=medium + + * url: Always encode whitespace characters. Particularly egregiously, + mLib used to leave linefeeds unescaped in `lax' mode. + * bits: Document the many improvements since the original version nearly + 20 years ago. + * bits: Add macros for byte-swapping integers in-place. + * bits: Use compiler intrinsics where available. + + -- Mark Wooding Tue, 12 Jun 2018 00:30:47 +0100 + mlib (2.2.5) experimental; urgency=medium * ident: Only close the socket once if connection fails early. diff --git a/utils/Makefile.am b/utils/Makefile.am index b52e7f6..2f6ed7c 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -32,6 +32,10 @@ libutils_la_SOURCES = ###-------------------------------------------------------------------------- ### Component files. +## Compiler checking. +pkginclude_HEADERS += compiler.h +LIBMANS += compiler.3 + ## Utility macros. pkginclude_HEADERS += macros.h LIBMANS += macros.3 diff --git a/utils/bits.3 b/utils/bits.3 index fc2885d..a002e24 100644 --- a/utils/bits.3 +++ b/utils/bits.3 @@ -1,24 +1,121 @@ .\" -*-nroff-*- .TH bits 3 "20 June 1999" "Straylight/Edgeware" "mLib utilities library" +.ie t \{\ +. ds ss \s8\u +. ds se \d\s0 +.\} +.el \{\ +. ds ss ^ +. ds se +.\} .SH NAME bits \- portable bit manipulation macros +.\" octet +.\" uint16 +.\" uint24 +.\" uint32 +.\" uint64 +.\" kludge64 +.\" +.\" MASK_8 +.\" MASK_16 +.\" MASK_16_L +.\" MASK_16_B +.\" MASK_24 +.\" MASK_24_L +.\" MASK_24_B +.\" MASK_32 +.\" MASK_32_L +.\" MASK_32_B +.\" MASK_64 +.\" MASK_64_L +.\" MASK_64_B +.\" +.\" SZ_8 +.\" SZ_16 +.\" SZ_16_L +.\" SZ_16_B +.\" SZ_24 +.\" SZ_24_L +.\" SZ_24_B +.\" SZ_32 +.\" SZ_32_L +.\" SZ_32_B +.\" SZ_64 +.\" SZ_64_L +.\" SZ_64_B +.\" +.\" TY_8 +.\" TY_16 +.\" TY_16_L +.\" TY_16_B +.\" TY_24 +.\" TY_24_L +.\" TY_24_B +.\" TY_32 +.\" TY_32_L +.\" TY_32_B +.\" TY_64 +.\" TY_64_L +.\" TY_64_B +.\" +.\" DOUINTSZ +.\" DOUINTCONV +.\" .\" @U8 .\" @U16 +.\" @U24 .\" @U32 +.\" @U64 +.\" @U64_ .\" .\" @LSL8 .\" @LSR8 .\" @LSL16 .\" @LSR16 +.\" @LSL24 +.\" @LSR24 .\" @LSL32 .\" @LSR32 +.\" @LSL64 +.\" @LSR64 +.\" @LSL64_ +.\" @LSR64_ .\" .\" @ROL8 .\" @ROR8 .\" @ROL16 .\" @ROR16 +.\" @ROL24 +.\" @ROR24 .\" @ROL32 -.\" @ROR32 +.\" @ROL32 +.\" @ROL64 +.\" @ROR64 +.\" @ROL64_ +.\" @ROR64_ +.\" +.\" ENDSWAP16 +.\" ENDSWAP32 +.\" ENDSWAP64 +.\" +.\" BTOH16 +.\" LTOH16 +.\" HTOB16 +.\" HTOL16 +.\" BTOH32 +.\" LTOH32 +.\" HTOB32 +.\" HTOL32 +.\" BTOH64 +.\" LTOH64 +.\" HTOB64 +.\" HTOL64 +.\" +.\" RAW8 +.\" RAW16 +.\" RAW32 +.\" RAW64 .\" .\" @GETBYTE .\" @PUTBYTE @@ -33,6 +130,13 @@ bits \- portable bit manipulation macros .\" @STORE16_B .\" @STORE16 .\" +.\" @LOAD24_L +.\" @LOAD24_B +.\" @LOAD24 +.\" @STORE24_L +.\" @STORE24_B +.\" @STORE24 +.\" .\" @LOAD32_L .\" @LOAD32_B .\" @LOAD32 @@ -40,54 +144,100 @@ bits \- portable bit manipulation macros .\" @STORE32_B .\" @STORE32 .\" +.\" @LOAD64_L +.\" @LOAD64_B +.\" @LOAD64 +.\" @STORE64_L +.\" @STORE64_B +.\" @STORE64 +.\" +.\" @LOAD64_L_ +.\" @LOAD64_B_ +.\" @LOAD64_ +.\" @STORE64_L_ +.\" @STORE64_B_ +.\" @STORE64_ +.\" +.\" @SET64 +.\" @X64 +.\" @ASSIGN64 +.\" @HI64 +.\" @LO64 +.\" @GET64 +.\" @AND64 +.\" @OR64 +.\" @XOR64 +.\" @CPL64 +.\" @ADD64 +.\" @SUB64 +.\" @CMP64 +.\" @ZERO64 .SH SYNOPSIS .nf .B "#include " -.BI "octet U8(" v ); -.BI "uint16 U16(" v ); -.BI "uint32 U32(" v ); +.BR "typedef " ... " octet;" +.BR "typedef " ... " uint16;" +.BR "typedef " ... " uint24;" +.BR "typedef " ... " uint32;" +.BR "typedef " ... " uint64;" +.BR "typedef " ... " kludge64;" -.BI "octet LSL8(" v ", " s ); -.BI "octet LSR8(" v ", " s ); -.BI "uint16 LSL16(" v ", " s ); -.BI "uint16 LSR16(" v ", " s ); -.BI "uint32 LSL32(" v ", " s ); -.BI "uint32 LSR32(" v ", " s ); +.BI "#define TY_" we " " type +.BI "#define SZ_" we " \fR..." +.BI "#define MASK_" we " \fR..." -.BI "octet ROL8(" v ", " s ); -.BI "octet ROR8(" v ", " s ); -.BI "uint16 ROL16(" v ", " s ); -.BI "uint16 ROR16(" v ", " s ); -.BI "uint32 ROL32(" v ", " s ); -.BI "uint32 ROR32(" v ", " s ); +.BI "#define DOUINTSZ(" f ") \fR..." +.BI "#define DOUINTCONV(" f ") \fR..." -.BI "octet GETBYTE(" p ", " o ); -.BI "void PUTBYTE(" p ", " o ", " v ); +.IB type " U" w ( v ); -.BI "octet LOAD8(" p ); -.BI "void STORE8(" p ", " v ); +.IB type " LSL" w ( type " " v ", int " s ); +.IB type " LSR" w ( type " " v ", int " s ); +.IB type " ROL" w ( type " " v ", int " s ); +.IB type " ROR" w ( type " " v ", int " s ); -.BI "uint16 LOAD16_B(" p ); -.BI "uint16 LOAD16_L(" p ); -.BI "uint16 LOAD16(" p ); -.BI "void STORE16_B(" p ", " v ); -.BI "void STORE16_L(" p ", " v ); -.BI "void STORE16(" p ", " v ); +.BI "octet GETBYTE(void *" p ", size_t " o ); +.BI "void PUTBYTE(void *" p ", size_t " o ", octet " v ); -.BI "uint32 LOAD32_B(" p ); -.BI "uint32 LOAD32_L(" p ); -.BI "uint32 LOAD32(" p ); -.BI "void STORE32_B(" p ", " v ); -.BI "void STORE32_L(" p ", " v ); -.BI "void STORE32(" p ", " v ); +.IB type " LOAD" we "(void *" p ); +.BI "void STORE" we "(void *" p ", " type " " v ); + +.BI "void SET64(kludge64 &" d ", uint32 " h ", uint32 " l ); +.BI "kludge64 X64(" hexh ", " hexl ); +.BI "void ASSIGN64(kludge64 &" d ", " x ); +.BI "uint32 HI64(kludge64" x ); +.BI "uint32 LO64(kludge64" x ); +.IB ty " GET64(" ty ", kludge64 " x ); +.BI "void AND64(kludge64 &" d ", kludge64 " x ", kludge64 " y ); +.BI "void OR64(kludge64 &" d ", kludge64 " x ", kludge64 " y ); +.BI "void XOR64(kludge64 &" d ", kludge64 " x ", kludge64 " y ); +.BI "void CPL64(kludge64 &" d ", kludge64 " x ); +.BI "void ADD64(kludge64 &" d ", kludge64 " x ", kludge64 " y ); +.BI "void SUB64(kludge64 &" d ", kludge64 " x ", kludge64 " y ); +.BI "int CMP64(kludge64 " x ", " op ", kludge64 " y ); +.BI "int ZERO64(kludge64 " x ); .fi .SH DESCRIPTION The header file .B contains a number of useful definitions for portably dealing with bit- -and byte-level manipulation of larger quantities. It declares three -types: +and byte-level manipulation of larger quantities. The various macros +and types are named fairly systematically. +.PP +The header provides utilities for working with 64-bit quantities, but a +64-bit integer type is not guaranteed to exist under C89 rules. This +header takes two approaches. Firstly, if a 64-bit type is found, the +header defines the macro +.B HAVE_UINT64 +and defines the various +.RB ... 64 +macros as described below. Secondly, it unconditionally defines a type +.B kludge64 +and a family of macros for working with them. See below for details. +. +.SS "Type definitions" +A number of types are defined. .TP .B octet Equivalent to @@ -105,106 +255,457 @@ Intended to be used when a 16-bit value is required. This type is always capable of representing any 16-bit unsigned value, but the actual type may be wider than 16 bits and will require masking. .TP +.B uint24 +Equivalent to some (architecture-dependent) standard type. Capable of +representing any unsigned 24-bit value, although the the actual type may +be wider than 24 bits. +.TP .B uint32 Equivalent to some (architecture-dependent) standard type. Capable of representing any unsigned 32-bit value, although the the actual type may be wider than 32 bits. +pp.TP +.B uint64 +Equivalent to some (architecture-dependent) standard type, if it exists. +Capable of representing any unsigned 64-bit value, although the the +actual type may be wider than 64 bits. +. +.SS "Size/endianness suffixes" +Let +.I w +be one of the size suffixes: 8, 16, 24, 32, and (if available) 64. +Furthermore, let +.I we +be one of the size-and-endian suffixes +.IR w , +or, where +.IR w \~>\~8, +.IB w _L +or +.IB w _B \fR, +where +.RB ` _L ' +denotes little-endian (Intel, VAX) representation, and +.RB ` _B ' +denotes big-endian (IBM, network) representation; omitting an explicit +suffix gives big-endian order by default, since this is most common in +portable data formats. .PP -The constants -.BR MASK8 , -.B MASK16 -and -.B MASK32 -contain bitmasks appropriate for discarding additional bits from a value -before use as an 8-, 16- or 32-bit quantity. The macros -.BR U8 , -.B U16 -and -.B U32 -perform masking and type-coercion on a value, and may be more useful in -general. For example, -.B U16(x) -yields a value of type -.B uint16 -which contains (only) the least-significant 16 bits of -.BR x . +The macro invocation +.BI DOUINTSZ( f ) +invokes a given macro +.I f +repeatedly, as +.IB f ( w ) +for each size suffix +.I w +listed above. .PP -The macros -.BI LSL n -and -.BI LSR n -perform left and right logical shift operations on values of width -.IR n , +The macro invocation +.BI DOUINTCONV( f ) +invokes a given macro +.I f +repeatedly, as +.IR f ( w ", " we ", " suff ) where -.I n -is one of -.BR 8 , -.B 16 +.I we +ranges over size-and-endian suffixes as described above, +.I w +is just the corresponding bit width, as an integer, and +.I suff +is a suffix +.IR w , +.IB w l\fR, or -.BR 32 . -The first argument, written -.IR v , -is the value to shift, and the second, written -.IR s , -is the number of bits to shift. The value -.I s -is reduced modulo -.I n -before use. Similarly, the macros -.BI ROL n -and -.BI ROR n -perform left and right rotations (cyclic shifts) on values of width -.IR n . -These macros are all written in such a way as to maximize portability. -A compiler may be able to spot that simple machine instructions will -suffice in many cases, although that's not the main objective. +.IB w b\fR, +suitable for a C function name. +.PP +These macros are intended to be used to define families of related +functions. +. +.SS "Utility macros" +For each size-and-endian suffix +.IR we , +the following macros are defined. +.TP +.BI TY_ we +A synonym for the appropriate one of the types +.BR octet , +.BR uint32 , +etc.\& listed above. +.TP +.BI SZ_ we +The number of octets needed to represent a value of the corresponding +type; i.e., this is +.IR w /8. +.TP +.BI MASK_ we +The largest integer representable in the corresponding type; i.e., this +is +.RI 2\*(ss w \*(se\~\-\~1. .PP -The macros -.BI LOAD n +(Note that the endianness suffix is irrelevant in the above +definitions.) +.PP +For each size suffix +.IR w , +the macro invocation +.BI U w ( x ) +coerces an integer +.I x +to the appropriate type; specifically, it returns the smallest +nonnegative integer congruent to +.I x +(modulo +.RI 2\*(ss w \*(se). +. +.SS "Shift and rotate" +For each size suffix +.IR w , +the macro invocations +.BI LSL w ( x ", " n ) and -.BI STORE n -(where again +.BI LSR w ( x ", " n ) +shift a +.IR w -bit +quantity +.I x +left or right, respectively, by .I n -is one of -.BR 8 , -.B 16 -or -.BR 32 ) -perform transformations between -.IR n -bit -quantities and arrays of octets. For example, -.B LOAD32(q) -returns the 32-bit value stored in the four octets starting at address -.BR q , -and -.B STORE16(q, x) -stores the 16-bit value -.B x -in the 2 octets starting at address -.BR q . -Values are loaded and stored such that the most significant octet is -assigned the lowest address (i.e., they use network, or big-endian byte -order). Macros -.BI LOAD n _L +places; if +.IR n \~\(>=\~ w +then +.I n +is reduced modulo +.IR w . +(This behaviour is unfortunate, but (a) it's what a number of CPUs +provide natively, and (b) it's a cheap way to prevent undefined +behaviour.) Similarly, +.BI ROL w ( x ", " n ) and -.BI STORE n _L -are also provided for +.BI ROR w ( x ", " n ) +rotate a +.IR w -bit +quantity +.I x +left or right, respectively, by .I n -either -.B 16 +places. +. +.SS "Byte order conversions" +For each size suffix +.IR w , +the macro invocation +.BI ENDSWAP w ( x ) +returns the +.IR w -bit +value +.IR x +with its bytes reversed. The +.B ENDSWAP8 +macro does nothing (except truncate its operand to 8 bits), but is +provided for the sake of completeness. +.PP +A +.I big-endian +representation stores the most significant octet of an integer at the +lowest address, with the following octets in decreasing order of +significance. A +.I little-endian +representation instead stores the +.I least +significant octet at the lowest address, with the following octets in +increasing order of significance. An environment has a preferred order +for arranging the constituent octets of an integer of some given size in +memory; this might be either the big- or little-endian representation +just described, or something else strange. +.PP +It might be possible to rearrange the bits in an integer so that, when +that integer is stored to memory in the environment's preferred manner, +you end up with the big- or little-endian representation of the original +integer; and, similarly, it might be possible to load a big- or +little-endian representation of an integer into a variable using the +environment's preferred ordering and then rearrange the bits so as to +recover the integer value originally represented. If the environment is +sufficiently strange, these things might not be possible, but this is +actually quite rare. +.PP +Say that an integer has been converted to +.I big- +or +.I "little-endian form" +if, when it is stored in memory in the environment's preferred manner, +one ends up with a big- or little-endian representation of the original +integer. Equivalently, if one starts with a big- or little-endian +representation of some integer, and loads it into a variable using the +environment's preferred manner, one ends up with the big- or +little-endian form of the original integer. +.PP +If these things are possible, then the following macros are defined. +.TP +.BI HTOL w ( x ) +Convert a +.IR w -bit +integer +.I x +to little-endian form. +.TP +.BI HTOB w ( x ) +Convert a +.IR w -bit +integer +.I x +to big-endian form. +.TP +.BI LTOH w ( x ) +Convert a +.IR w -bit +integer +.I x +from little-endian form. +.TP +.BI BTOH w ( x ) +Convert a +.IR w -bit +integer +.I x +from big-endian form. +. +.SS "Load and store" +The macro invocation +.BI GETBYTE( p ", " o ) +returns the +.IR o th +octet following the address +.IR p . +Conversely, +.BI PUTBYTE( p ", " o ", " v) +stores +.I +v in the +.IR o th +byte following the address +.IR p . +These macros always operate on byte offsets regardless of the type of +the pointer +.IR p . +.PP +For each size suffix +.IR w , +there may be a macro such that the invocation +.BI RAW w ( p ) +is an lvalue designating the +.IR w /8 +octets starting at address +.IR p , +interpreted according to the environment's preferred representation, +except that +.I p +need not be aligned in any particular fashion. There are many reasons +why this might not be possible; programmers are not normally expected to +use these macros directly, and they are documented in case they are +useful for special effects. +.PP +For each size-and-endian suffix +.IR we , +the macro invocation +.BI LOAD we ( p ) +loads and returns a value in the corresponding format at address +.IR p ; +similarly, +.BI STORE we ( p ", " x ) +stores the value +.I x +at address +.I p +in the corresponding format. +. +.SS "64-bit support" +For portability to environments without native 64-bit integers, the +structure +.B kludge64 +is defined. If the target platform is known to have an unsigned 64-bit +integer type, then this structure merely encapsulates a native integer, +and a decent optimizing compiler can be expected to handle this exactly +as if it were the native type. Otherwise, it contains two 32-bit halves +which are processed the hard way. +.PP +For each of the above macros with a suffix +.BR 64 , +.BR 64_L , or -.BR 32 : -they use little-endian byte order. There are -explicit big-endian macros -.BI LOAD n _B +.BR 64_B , +an additional `kludge' macro is defined, whose name has an additional +final underscore; e.g., the kludge macro corresponding to +.B ROR64 +is +.BR ROR64_ ; +and that corresponding to +.B LOAD64_L +is +.BR LOAD64_L_ . +If the original macro would have +.I returned +a value of type +.BR uint64 , +then the kludge macro has an additional first argument, denoted +.IR d , +which should be an lvalue of type +.BR kludge64 , +and the kludge macro will store its result in +.IR d . +The kludge macro's remaining arguments are the same as the original +macro, except that where the original macro accepts an argument of type +.BR uint64 , +the kludge macro accepts an argument of type +.B kludge64 +instead. +.PP +Finally, a number of additional macros are provided, to make working +with +.B kludge64 +somewhat less awful. +.TP +.BI SET64( d ", " h ", " l ) +Set the high 32 bits of +.I d +to be +.IR h , +and the low 32 bits to be +.IR l . +Both +.I h +and +.I l +may be arbitrary integers. +.TP +.BI X64( hexh ", " hexl ) +Expands to an initializer for an object of type +.B kludge64 +where +.I hexh +and +.I hexl +encode the high and low 32-bit halves in hexadecimal, without any +.B 0x +prefix. +.TP +.BI ASSIGN( d ", " x ) +Make +.I d +be a copy of the +.B kludge64 +.IR x . +.TP +.BI HI64( x ) +Return the high 32 bits of +.IR x . +.TP +.BI LO64( x ) +Return the low 32 bits of +.IR x . +.TP +.BI GET64( t ", " x ) +Return the value of +.I x +as a value of type +.IR t . +If +.I t +is an unsigned integer type, then the value will be truncated to fit as +necessary; if +.I t +is a signed integer type, then the behaviour is undefined if the value +of +.I x +is too large. +.TP +.BI AND64( d ", " x ", " y ) +Set +.I d +to be the bitwise-and of the two +.B kludge64 +arguments +.I x +and +.IR y . +.TP +.BI OR64( d ", " x ", " y ) +Set +.I d +to be the bitwise-or of the two +.B kludge64 +arguments +.I x +and +.IR y . +.TP +.BI XOR64( d ", " x ", " y ) +Set +.I d +to be the bitwise-exclusive-or of the two +.B kludge64 +arguments +.I x +and +.IR y . +.TP +.BI CPL64( d ", " x ) +Set +.I d +to be the bitwise complement of the +.B kludge64 +argument +.IR x . +.TP +.BI ADD64( d ", " x ", " y ) +Set +.I d +to be the sum of the two +.B kludge64 +arguments +.I x and -.BI STORE n _B -too. The pointer arguments don't have to be pointers to octets; the -value arguments don't have to be of the right type. The macros perform -all appropriate type casting and masking. Again, these macros are -written with portability foremost in mind, although it seems they don't -actually perform at all badly in real use. +.IR y . +.TP +.BI SUB64( d ", " x ", " y ) +Set +.I d +to be the difference of the two +.B kludge64 +arguments +.I x +and +.IR y . +.TP +.BI CMP64( x ", " op ", " y ) +Here, +.I x +and +.I y +should be arguments of type +.B kludge64 +and +.I op +should be one of the relational operators +.BR == , +.BR < , +.BR <= , +.BR > , +or +.B >= +\(en +.I not +.BR !=. +Evaluates nonzero if +.IR x \~ op \~ y . +.TP +.BI ZERO64( x ) +Evaluates nonzero if the +.B kludge64 +argument +.I x +is exactly zero. .SH "SEE ALSO" .BR mLib (3). .SH AUTHOR diff --git a/utils/bits.h b/utils/bits.h index f3fe08f..656eee5 100644 --- a/utils/bits.h +++ b/utils/bits.h @@ -40,6 +40,10 @@ # include #endif +#ifndef MLIB_COMPILER_H +# include "compiler.h" +#endif + /*----- Decide on some types ----------------------------------------------*/ /* --- Make GNU C shut up --- */ @@ -314,117 +318,283 @@ typedef unsigned char octet, uint8; } while (0) #endif +/* --- Endianness swapping --- */ + +#if GCC_VERSION_P(4, 8) +# define ENDSWAP16(x) ((uint16)__builtin_bswap16(x)) +#endif +#if GCC_VERSION_P(4, 3) +# define ENDSWAP32(x) ((uint32)__builtin_bswap32(x)) +#endif +#if GCC_VERSION_P(4, 3) && defined(HAVE_UINT64) +# define ENDSWAP64(x) ((uint64)__builtin_bswap64(x)) +#endif + +#ifndef ENDSWAP8 +# define ENDSWAP8(x) U8(x) +#endif +#ifndef ENDSWAP16 +# define ENDSWAP16(x) \ + ((((uint16)(x) >> 8)&0xff) | \ + (((uint16)(x)&0xff) << 8)) +#endif +#ifndef ENDSWAP24 +# define ENDSWAP24(x) \ + ((((uint24)(x) >> 16)&0xff) | \ + ((uint24)(x)&0xff00) | \ + ((uint24)((x)&0xff) << 16)) +#endif +#ifndef ENDSWAP32 +# define ENDSWAP32(x) \ + (ENDSWAP16(((uint32)(x) >> 16)&0xffff) | \ + ((uint32)ENDSWAP16((x)&0xffff) << 16)) +#endif +#if defined(HAVE_UINT64) && !defined(ENDSWAP64) +# define ENDSWAP64(x) \ + (ENDSWAP32(((uint64)(x) >> 32)&0xffffffff) | \ + ((uint64)ENDSWAP32((x)&0xffffffff) << 32)) +#endif +#ifdef HAVE_UINT64 +# define ENDSWAP64_(z, x) \ + ((z).i = ENDSWAP64((x).i)) +#else +# define ENDSWAP64_(z, x) \ + ((z).lo = ENDSWAP32((x).hi), \ + (z).hi = ENDSWAP32((x).lo)) +#endif + +#define MLIB_LITTLE_ENDIAN 1234 +#define MLIB_BIG_ENDIAN 4321 +#if defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define MLIB_BYTE_ORDER MLIB_LITTLE_ENDIAN +#elif defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define MLIB_BYTE_ORDER MLIB_BIG_ENDIAN +#endif + +#if MLIB_BYTE_ORDER == MLIB_LITTLE_ENDIAN +# define HTOL16(x) (x) +# define LTOH16(x) (x) +# define HTOB16(x) ENDSWAP16(x) +# define BTOH16(x) ENDSWAP16(x) +# define HTOL24(x) (x) +# define LTOH24(x) (x) +# define HTOB24(x) ENDSWAP24(x) +# define BTOH24(x) ENDSWAP24(x) +# define HTOL32(x) (x) +# define LTOH32(x) (x) +# define HTOB32(x) ENDSWAP32(x) +# define BTOH32(x) ENDSWAP32(x) +# ifdef HAVE_UINT64 +# define HTOL64(x) (x) +# define LTOH64(x) (x) +# define HTOB64(x) ENDSWAP64(x) +# define BTOH64(x) ENDSWAP64(x) +# endif +# define HTOL64_(z, x) ASSIGN64(z, x) +# define LTOH64_(z, x) ASSIGN64(z, x) +# define HTOB64_(z, x) ENDSWAP64_(z, x) +# define BTOH64_(z, x) ENDSWAP64_(z, x) +#elif MLIB_BYTE_ORDER == MLIB_BIG_ENDIAN +# define HTOL16(x) ENDSWAP16(x) +# define LTOH16(x) ENDSWAP16(x) +# define HTOB16(x) (x) +# define BTOH16(x) (x) +# define HTOL24(x) ENDSWAP24(x) +# define LTOH24(x) ENDSWAP24(x) +# define HTOB24(x) (x) +# define BTOH24(x) (x) +# define HTOL32(x) ENDSWAP32(x) +# define LTOH32(x) ENDSWAP32(x) +# define HTOB32(x) (x) +# define BTOH32(x) (x) +# ifdef HAVE_UINT64 +# define HTOL64(x) ENDSWAP64(x) +# define LTOH64(x) ENDSWAP64(x) +# define HTOB64(x) (x) +# define BTOH64(x) (x) +# define HTOL64_(z, x) ENDSWAP64_(z, x) +# define LTOH64_(z, x) ENDSWAP64_(z, x) +# define HTOB64_(z, x) ((z).i = (x).i) +# define BTOH64_(z, x) ((z).i = (x).i) +# endif +# define HTOL64_(z, x) ENDSWAP64_(z, x) +# define LTOH64_(z, x) ENDSWAP64_(z, x) +# define HTOB64_(z, x) ASSIGN64(z, x) +# define BTOH64_(z, x) ASSIGN64(z, x) +#endif + +/* --- Unaligned access (GCC-specific) --- */ + +#if GCC_VERSION_P(3, 3) && CHAR_BIT == 8 +# define MLIB_MISALIGNED __attribute__((aligned(1), may_alias)) +# if __SIZEOF_SHORT__ == 2 + typedef MLIB_MISALIGNED unsigned short misaligned_uint16; +# define RAW16(p) (*(misaligned_uint16 *)(p)) +# endif +# if __SIZEOF_INT__ == 4 + typedef MLIB_MISALIGNED unsigned int misaligned_uint32; +# define RAW32(p) (*(misaligned_uint32 *)(p)) +# elif __SIZEOF_LONG__ == 4 + typedef MLIB_MISALIGNED unsigned long misaligned_uint32; +# define RAW32(p) (*(misaligned_uint32 *)(p)) +# endif +# if __SIZEOF_LONG__ == 8 + typedef MLIB_MISALIGNED unsigned long misaligned_uint64; +# define RAW64(p) (*(misaligned_uint64 *)(p)) +# elif __SIZEOF_LONG_LONG__ == 8 + typedef MLIB_MISALIGNED unsigned long long misaligned_uint64; +# define RAW64(p) (*(misaligned_uint64 *)(p)) +# endif +#endif + /* --- Storage and retrieval --- */ +#if defined(RAW16) && defined(LTOH16) +# define LOAD16_L(p) LTOH16(RAW16(p)) +#endif +#if defined(RAW16) && defined(HTOL16) +# define STORE16_L(p, x) (RAW16(p) = HTOL16(x)) +#endif +#if defined(RAW16) && defined(BTOH16) +# define LOAD16_B(p) BTOH16(RAW16(p)) +#endif +#if defined(RAW16) && defined(HTOB16) +# define STORE16_B(p, x) (RAW16(p) = HTOB16(x)) +#endif + +#if defined(RAW32) && defined(LTOH32) +# define LOAD32_L(p) LTOH32(RAW32(p)) +#endif +#if defined(RAW32) && defined(HTOL32) +# define STORE32_L(p, x) (RAW32(p) = HTOL32(x)) +#endif +#if defined(RAW32) && defined(BTOH32) +# define LOAD32_B(p) BTOH32(RAW32(p)) +#endif +#if defined(RAW32) && defined(HTOB32) +# define STORE32_B(p, x) (RAW32(p) = HTOB32(x)) +#endif + +#if defined(RAW64) && defined(LTOH64) +# define LOAD64_L(p) LTOH64(RAW64(p)) +#endif +#if defined(RAW64) && defined(HTOL64) +# define STORE64_L(p, x) (RAW64(p) = HTOL64(x)) +#endif +#if defined(RAW64) && defined(BTOH64) +# define LOAD64_B(p) BTOH64(RAW64(p)) +#endif +#if defined(RAW64) && defined(HTOB64) +# define STORE64_B(p, x) (RAW64(p) = HTOB64(x)) +#endif + #define GETBYTE(p, o) (((octet *)(p))[o] & MASK8) #define PUTBYTE(p, o, v) (((octet *)(p))[o] = U8((v))) #define LOAD8(p) (GETBYTE((p), 0)) #define STORE8(p, v) (PUTBYTE((p), 0, (v))) -#define LOAD16_B(p) \ - (((uint16)GETBYTE((p), 0) << 8) | \ - ((uint16)GETBYTE((p), 1) << 0)) -#define LOAD16_L(p) \ - (((uint16)GETBYTE((p), 0) << 0) | \ - ((uint16)GETBYTE((p), 1) << 8)) +#ifndef LOAD16_B +# define LOAD16_B(p) + (((uint16)GETBYTE((p), 0) << 8) | \ + ((uint16)GETBYTE((p), 1) << 0)) +#endif +#ifndef LOAD16_L +# define LOAD16_L(p) \ + (((uint16)GETBYTE((p), 0) << 0) | \ + ((uint16)GETBYTE((p), 1) << 8)) +#endif #define LOAD16(p) LOAD16_B((p)) -#define STORE16_B(p, v) \ - (PUTBYTE((p), 0, (uint16)(v) >> 8), \ - PUTBYTE((p), 1, (uint16)(v) >> 0)) -#define STORE16_L(p, v) \ - (PUTBYTE((p), 0, (uint16)(v) >> 0), \ - PUTBYTE((p), 1, (uint16)(v) >> 8)) +#ifndef STORE16_B +# define STORE16_B(p, v) \ + (PUTBYTE((p), 0, (uint16)(v) >> 8), \ + PUTBYTE((p), 1, (uint16)(v) >> 0)) +#endif +#ifndef STORE16_L +# define STORE16_L(p, v) \ + (PUTBYTE((p), 0, (uint16)(v) >> 0), \ + PUTBYTE((p), 1, (uint16)(v) >> 8)) +#endif #define STORE16(p, v) STORE16_B((p), (v)) -#define LOAD24_B(p) \ - (((uint24)GETBYTE((p), 0) << 16) | \ - ((uint24)GETBYTE((p), 1) << 8) | \ - ((uint24)GETBYTE((p), 2) << 0)) -#define LOAD24_L(p) \ - (((uint24)GETBYTE((p), 0) << 0) | \ - ((uint24)GETBYTE((p), 1) << 8) | \ - ((uint24)GETBYTE((p), 2) << 16)) +#ifndef LOAD24_B +# define LOAD24_B(p) \ + (((uint24)GETBYTE((p), 0) << 16) | \ + ((uint24)LOAD16_B((octet *)(p) + 1) << 0)) +#endif +#ifndef LOAD24_L +# define LOAD24_L(p) \ + (((uint24)LOAD16_L((octet *)(p) + 0) << 0) | \ + ((uint24)GETBYTE((p), 2) << 16)) +#endif #define LOAD24(p) LOAD24_B((p)) -#define STORE24_B(p, v) \ - (PUTBYTE((p), 0, (uint24)(v) >> 16), \ - PUTBYTE((p), 1, (uint24)(v) >> 8), \ - PUTBYTE((p), 2, (uint24)(v) >> 0)) -#define STORE24_L(p, v) \ - (PUTBYTE((p), 0, (uint24)(v) >> 0), \ - PUTBYTE((p), 1, (uint24)(v) >> 8), \ - PUTBYTE((p), 2, (uint24)(v) >> 16)) +#ifndef STORE24_B +# define STORE24_B(p, v) \ + (PUTBYTE((p), 0, (uint24)(v) >> 16), \ + STORE16_B((octet *)(p) + 1, (uint24)(v) >> 0)) +#endif +#ifndef STORE24_L +# define STORE24_L(p, v) \ + (STORE16_L((octet *)(p) + 0, (uint24)(v) >> 0), \ + PUTBYTE((p), 2, (uint24)(v) >> 16)) +#endif #define STORE24(p, v) STORE24_B((p), (v)) -#define LOAD32_B(p) \ - (((uint32)GETBYTE((p), 0) << 24) | \ - ((uint32)GETBYTE((p), 1) << 16) | \ - ((uint32)GETBYTE((p), 2) << 8) | \ - ((uint32)GETBYTE((p), 3) << 0)) -#define LOAD32_L(p) \ - (((uint32)GETBYTE((p), 0) << 0) | \ - ((uint32)GETBYTE((p), 1) << 8) | \ - ((uint32)GETBYTE((p), 2) << 16) | \ - ((uint32)GETBYTE((p), 3) << 24)) +#ifndef LOAD32_B +# define LOAD32_B(p) \ + (((uint32)LOAD16_B((octet *)(p) + 0) << 16) | \ + ((uint32)LOAD16_B((octet *)(p) + 2) << 0)) +#endif +#ifndef LOAD32_L +# define LOAD32_L(p) \ + (((uint32)LOAD16_L((octet *)(p) + 0) << 0) | \ + ((uint32)LOAD16_L((octet *)(p) + 2) << 16)) +#endif #define LOAD32(p) LOAD32_B((p)) -#define STORE32_B(p, v) \ - (PUTBYTE((p), 0, (uint32)(v) >> 24), \ - PUTBYTE((p), 1, (uint32)(v) >> 16), \ - PUTBYTE((p), 2, (uint32)(v) >> 8), \ - PUTBYTE((p), 3, (uint32)(v) >> 0)) -#define STORE32_L(p, v) \ - (PUTBYTE((p), 0, (uint32)(v) >> 0), \ - PUTBYTE((p), 1, (uint32)(v) >> 8), \ - PUTBYTE((p), 2, (uint32)(v) >> 16), \ - PUTBYTE((p), 3, (uint32)(v) >> 24)) +#ifndef STORE32_B +# define STORE32_B(p, v) \ + (STORE16_B((octet *)(p) + 0, (uint32)(v) >> 16), \ + STORE16_B((octet *)(p) + 2, (uint32)(v) >> 0)) +#endif +#ifndef STORE32_L +# define STORE32_L(p, v) \ + (STORE16_L((octet *)(p) + 0, (uint32)(v) >> 0), \ + STORE16_L((octet *)(p) + 2, (uint32)(v) >> 16)) +#endif #define STORE32(p, v) STORE32_B((p), (v)) #ifdef HAVE_UINT64 -# define LOAD64_B(p) \ - (((uint64)GETBYTE((p), 0) << 56) | \ - ((uint64)GETBYTE((p), 1) << 48) | \ - ((uint64)GETBYTE((p), 2) << 40) | \ - ((uint64)GETBYTE((p), 3) << 32) | \ - ((uint64)GETBYTE((p), 4) << 24) | \ - ((uint64)GETBYTE((p), 5) << 16) | \ - ((uint64)GETBYTE((p), 6) << 8) | \ - ((uint64)GETBYTE((p), 7) << 0)) -# define LOAD64_L(p) \ - (((uint64)GETBYTE((p), 0) << 0) | \ - ((uint64)GETBYTE((p), 1) << 8) | \ - ((uint64)GETBYTE((p), 2) << 16) | \ - ((uint64)GETBYTE((p), 3) << 24) | \ - ((uint64)GETBYTE((p), 4) << 32) | \ - ((uint64)GETBYTE((p), 5) << 40) | \ - ((uint64)GETBYTE((p), 6) << 48) | \ - ((uint64)GETBYTE((p), 7) << 56)) +# ifndef LOAD64_B +# define LOAD64_B(p) \ + (((uint64)LOAD32_B((octet *)(p) + 0) << 32) | \ + ((uint64)LOAD32_B((octet *)(p) + 4) << 0)) +# endif +# ifndef LOAD64_L +# define LOAD64_L(p) \ + (((uint64)LOAD32_L((octet *)(p) + 0) << 0) | \ + ((uint64)LOAD32_L((octet *)(p) + 4) << 32)) +# endif # define LOAD64(p) LOAD64_B((p)) # define LOAD64_B_(d, p) ((d).i = LOAD64_B((p))) # define LOAD64_L_(d, p) ((d).i = LOAD64_L((p))) # define LOAD64_(d, p) LOAD64_B_((d), (p)) -# define STORE64_B(p, v) \ - (PUTBYTE((p), 0, (uint64)(v) >> 56), \ - PUTBYTE((p), 1, (uint64)(v) >> 48), \ - PUTBYTE((p), 2, (uint64)(v) >> 40), \ - PUTBYTE((p), 3, (uint64)(v) >> 32), \ - PUTBYTE((p), 4, (uint64)(v) >> 24), \ - PUTBYTE((p), 5, (uint64)(v) >> 16), \ - PUTBYTE((p), 6, (uint64)(v) >> 8), \ - PUTBYTE((p), 7, (uint64)(v) >> 0)) -# define STORE64_L(p, v) \ - (PUTBYTE((p), 0, (uint64)(v) >> 0), \ - PUTBYTE((p), 1, (uint64)(v) >> 8), \ - PUTBYTE((p), 2, (uint64)(v) >> 16), \ - PUTBYTE((p), 3, (uint64)(v) >> 24), \ - PUTBYTE((p), 4, (uint64)(v) >> 32), \ - PUTBYTE((p), 5, (uint64)(v) >> 40), \ - PUTBYTE((p), 6, (uint64)(v) >> 48), \ - PUTBYTE((p), 7, (uint64)(v) >> 56)) +# ifndef STORE64_B +# define STORE64_B(p, v) \ + (STORE32_B((octet *)(p) + 0, (uint64)(v) >> 32), \ + STORE32_B((octet *)(p) + 4, (uint64)(v) >> 0)) +# endif +# ifndef STORE64_L +# define STORE64_L(p, v) \ + (STORE32_L((octet *)(p) + 0, (uint64)(v) >> 0), \ + STORE32_L((octet *)(p) + 4, (uint64)(v) >> 32)) +# endif # define STORE64(p, v) STORE64_B((p), (v)) # define STORE64_B_(p, v) STORE64_B((p), (v).i) # define STORE64_L_(p, v) STORE64_L((p), (v).i) @@ -433,19 +603,19 @@ typedef unsigned char octet, uint8; #else # define LOAD64_B_(d, p) \ - ((d).hi = LOAD32_B((octet *)(p) + 0), \ - (d).lo = LOAD32_B((octet *)(p) + 4)) + ((d).hi = LOAD32_B((octet *)(p) + 0), \ + (d).lo = LOAD32_B((octet *)(p) + 4)) # define LOAD64_L_(d, p) \ - ((d).lo = LOAD32_L((octet *)(p) + 0), \ - (d).hi = LOAD32_L((octet *)(p) + 4)) + ((d).lo = LOAD32_L((octet *)(p) + 0), \ + (d).hi = LOAD32_L((octet *)(p) + 4)) # define LOAD64_(d, p) LOAD64_B_((d), (p)) # define STORE64_B_(p, v) \ - (STORE32_B((octet *)(p) + 0, (v).hi), \ - STORE32_B((octet *)(p) + 4, (v).lo)) + (STORE32_B((octet *)(p) + 0, (v).hi), \ + STORE32_B((octet *)(p) + 4, (v).lo)) # define STORE64_L_(p, v) \ - (STORE32_L((octet *)(p) + 0, (v).lo), \ - STORE32_L((octet *)(p) + 4, (v).hi)) + (STORE32_L((octet *)(p) + 0, (v).lo), \ + STORE32_L((octet *)(p) + 4, (v).hi)) # define STORE64_(p, v) STORE64_B_((p), (v)) #endif diff --git a/utils/compiler.3 b/utils/compiler.3 new file mode 100644 index 0000000..1920be4 --- /dev/null +++ b/utils/compiler.3 @@ -0,0 +1,35 @@ +.\" -*-nroff-*- +.TH compiler 3 "26 May 2018" "Straylight/Edgeware" "mLib utilities library" +.SH NAME +compiler \- detect compiler version +.\" @GCC_VERSION_P +.\" @CLANG_VERSION_P +.SH SYNOPSIS +.nf +.B "#include " + +.BI "int GCC_VERSION_P(" maj ", " min ");" +.BI "int CLANG_VERSION_P(" maj ", " min ");" +.fi +.SH DESCRIPTION +The macro invocation +.BI GCC_VERSION_P( maj ", " min ) +expands to a compile-time constant nonzero value if the present compiler +is GCC version +.IR maj . min +or better, or claims compatibility with it. +This is frequently imperfect, as many compilers claim compatibility +without implementing all of the necessary features, but it works +adequately if one takes care. +.PP +The macro invocation +.BI CLANG_VERSION_P( maj ", " min ) +expands to a compile-time constant nonzero value if the present compiler +is Clang version +.IR maj . min +or better (or claims compatibility with it, but this is less likely +than for GCC. +.SH "SEE ALSO" +.BR mLib (3). +.SH "AUTHOR" +Mark Wooding, diff --git a/utils/compiler.h b/utils/compiler.h new file mode 100644 index 0000000..7e66d12 --- /dev/null +++ b/utils/compiler.h @@ -0,0 +1,58 @@ +/* -*-c-*- + * + * Macros for determining the compiler version + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software: you can redistribute it and/or modify it under + * the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * mLib is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + * License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef MLIB_COMPILER_H +#define MLIB_COMPILER_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Macros ------------------------------------------------------------*/ + +#if defined(__GNUC__) +# define GCC_VERSION_P(maj, min) \ + (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min))) +#else +# define GCC_VERSION_P(maj, min) 0 +#endif + +#ifdef __clang__ +# define CLANG_VERSION_P(maj, min) \ + (__clang_major__ > (maj) || (__clang_major__ == (maj) && \ + __clang_minor__ >= (min))) +#else +# define CLANG_VERSION_P(maj, min) 0 +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/utils/macros.3 b/utils/macros.3 index bd4145a..a2a9003 100644 --- a/utils/macros.3 +++ b/utils/macros.3 @@ -16,7 +16,6 @@ macros \- useful macros .\" @MUFFLE_WARNINGS_DECL .\" @MUFFLE_WARNINGS_EXPR .\" @MUFFLE_WARNINGS_STMT -.\" @GCC_VERSION_P .\" @GCC_WARNING .SH SYNOPSIS .nf @@ -40,8 +39,8 @@ macros \- useful macros .BI "MUFFLE_WARNINGS_EXPR(" warns ", " expr ")" .BI "MUFFLE_WARNINGS_STMT(" warns ", " stmt ")" -.BI "int GCC_VERSION_P(" maj ", " min ");" .BI "GCC_WARNING(" option ")" +.BI "CLANG_WARNING(" option ")" .fi .SH DESCRIPTION .SS Utilities @@ -163,7 +162,19 @@ lists (see above). It takes a string-literal argument .I option naming a GCC warning option, e.g., .BR """\-Wdiv-by-zero""" . +.PP +The +.B CLANG_WARNING +is similar, except that it works with the Clang compiler. +.PP +Note that including +.B +also defines the compiler-test macros in +.BR ; +see +.BR compiler (3). .SH "SEE ALSO" -.BR mLib (3). +.BR mLib (3), +.BR compiler (3). .SH "AUTHOR" Mark Wooding, diff --git a/utils/macros.h b/utils/macros.h index 02d820e..bcceecd 100644 --- a/utils/macros.h +++ b/utils/macros.h @@ -32,6 +32,12 @@ extern "C" { #endif +/*----- Header files ------------------------------------------------------*/ + +#ifndef MLIB_COMPILER_H +# include "compiler.h" +#endif + /*----- Miscellaneous utility macros --------------------------------------*/ #define N(v) (sizeof(v)/sizeof(*v)) @@ -46,21 +52,6 @@ /* --- Compiler-specific definitions --- */ -#if defined(__GNUC__) -# define GCC_VERSION_P(maj, min) \ - (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min))) -#else -# define GCC_VERSION_P(maj, min) 0 -#endif - -#ifdef __clang__ -# define CLANG_VERSION_P(maj, min) \ - (__clang_major__ > (maj) || (__clang_major__ == (maj) && \ - __clang_minor__ >= (min))) -#else -# define CLANG_VERSION_P(maj, min) 0 -#endif - #if GCC_VERSION_P(2, 5) || CLANG_VERSION_P(3, 3) # define NORETURN __attribute__((noreturn)) # define PRINTF_LIKE(fix, aix) __attribute__((format(printf, fix, aix)))