feat(ctype): classification and case-mapping tables
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* The "C" locale character-class table: one entry per unsigned char value,
|
||||
* holding the OR of the class bits below. The table is laid out as an
|
||||
* explicit 8-entries-per-row grid so each byte can be audited against the
|
||||
* ASCII ranges the standard specifies. The 0x80..0xff half is deliberately
|
||||
* not written: a static initializer zero-fills the elements it does not
|
||||
* mention, so every high byte — entry 255 included, which is where EOF
|
||||
* lands after the unsigned char cast — classifies false. Because every
|
||||
* lookup casts the argument to unsigned char first:
|
||||
*
|
||||
* - a negative char value can never index out of bounds;
|
||||
* - EOF classifies false in every table-driven is* function without a
|
||||
* special case;
|
||||
* - tolower/toupper pass EOF (and every non-letter) through unchanged,
|
||||
* returning the argument as given.
|
||||
*
|
||||
* isdigit, isxdigit and isprint are not table-driven: they are single
|
||||
* unsigned comparisons on the argument (see below), which makes them
|
||||
* genuinely const-capable — but only isdigit/isxdigit are declared const:
|
||||
* GCC predeclares isdigit/isxdigit as const builtins and isprint as a pure
|
||||
* builtin, and the header's attribute must match each builtin's declaration
|
||||
* or the weaker one is rejected with a warning.
|
||||
*/
|
||||
|
||||
#define CTYPE_UPPER 0x01U
|
||||
#define CTYPE_LOWER 0x02U
|
||||
#define CTYPE_DIGIT 0x04U
|
||||
#define CTYPE_CNTRL 0x08U
|
||||
#define CTYPE_PUNCT 0x10U
|
||||
#define CTYPE_SPACE 0x20U
|
||||
#define CTYPE_BLANK 0x80U
|
||||
|
||||
/* Combined masks, mirroring the standard's definitions. */
|
||||
#define CTYPE_ALPHA (CTYPE_UPPER | CTYPE_LOWER)
|
||||
#define CTYPE_ALNUM (CTYPE_ALPHA | CTYPE_DIGIT)
|
||||
#define CTYPE_GRAPH (CTYPE_ALNUM | CTYPE_PUNCT)
|
||||
|
||||
static const unsigned char ctype_class[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 */
|
||||
/* 0x00 */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
/* 0x08 */ 0x08, 0xa8, 0x28, 0x28, 0x28, 0x28, 0x08, 0x08,
|
||||
/* 0x10 */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
/* 0x18 */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
/* 0x20 */ 0xa0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x28 */ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x30 */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
/* 0x38 */ 0x04, 0x04, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x40 */ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
/* 0x48 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
/* 0x50 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
/* 0x58 */ 0x01, 0x01, 0x01, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x60 */ 0x10, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
/* 0x68 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
/* 0x70 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
/* 0x78 */ 0x02, 0x02, 0x02, 0x10, 0x10, 0x10, 0x10, 0x08,
|
||||
/* 0x80..0xff stay zero by the static initializer's default. */
|
||||
};
|
||||
|
||||
int
|
||||
isalnum(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_ALNUM) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isalpha(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_ALPHA) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isblank(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_BLANK) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscntrl(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_CNTRL) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pure arithmetic (no table): the unsigned cast turns every value below '0'
|
||||
* into a huge number, so exactly 0..9 pass the range check.
|
||||
*/
|
||||
int
|
||||
isdigit(int c)
|
||||
{
|
||||
return (unsigned int)c - '0' < 10U;
|
||||
}
|
||||
|
||||
int
|
||||
isgraph(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_GRAPH) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
islower(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_LOWER) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pure arithmetic (no table): the printable range 0x20..0x7e.
|
||||
*/
|
||||
int
|
||||
isprint(int c)
|
||||
{
|
||||
return (unsigned int)c - 0x20 < 0x5fU;
|
||||
}
|
||||
|
||||
int
|
||||
ispunct(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_PUNCT) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isspace(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_SPACE) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isupper(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_UPPER) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pure arithmetic (no table): c | 0x20 folds 'A'..'F' to 'a'..'f'; the
|
||||
* unsigned cast first keeps every negative value out of the range check.
|
||||
*/
|
||||
int
|
||||
isxdigit(int c)
|
||||
{
|
||||
return (unsigned int)c - '0' < 10U || (unsigned int)(c | 0x20) - 'a' < 6U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map an uppercase letter to lowercase and pass everything else — non-letters
|
||||
* and EOF alike — through unchanged. The class bit is authoritative, so the
|
||||
* unsigned char cast already keeps EOF and negative values out of the
|
||||
* mapping branch.
|
||||
*/
|
||||
int
|
||||
tolower(int c)
|
||||
{
|
||||
if ((ctype_class[(unsigned char)c] & CTYPE_UPPER) != 0)
|
||||
{
|
||||
return c + ('a' - 'A');
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
toupper(int c)
|
||||
{
|
||||
if ((ctype_class[(unsigned char)c] & CTYPE_LOWER) != 0)
|
||||
{
|
||||
return c - ('a' - 'A');
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* True when c is a 7-bit ASCII value. EOF and every other negative value are
|
||||
* outside the range, so they classify false without a cast.
|
||||
*/
|
||||
int
|
||||
isascii(int c)
|
||||
{
|
||||
return c >= 0 && c < 128;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* Clear every bit above the low 7 of c. Pure bit arithmetic: defined for
|
||||
* every int value, EOF included (toascii(-1) is 0x7f).
|
||||
*/
|
||||
int
|
||||
toascii(int c)
|
||||
{
|
||||
return c & 0x7f;
|
||||
}
|
||||
Reference in New Issue
Block a user