feat(ctype): classification and case-mapping tables
This commit is contained in:
+164
@@ -0,0 +1,164 @@
|
|||||||
|
#ifndef VLIBC_CTYPE_H
|
||||||
|
#define VLIBC_CTYPE_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vlibc — <ctype.h>.
|
||||||
|
*
|
||||||
|
* Character classification and case mapping. Every function takes an int
|
||||||
|
* that represents an unsigned char value or EOF (-1). The implementation
|
||||||
|
* always casts the argument to unsigned char before any table lookup, so a
|
||||||
|
* negative char value can never index out of bounds; EOF classifies false
|
||||||
|
* in every is* function, and tolower/toupper pass it (and every non-letter)
|
||||||
|
* through unchanged. All results are for the "C" locale.
|
||||||
|
*
|
||||||
|
* Levels are cumulative (see include/vlibc/features.h):
|
||||||
|
* Level 1 (onlyposix): ISO C — the classification and case-mapping
|
||||||
|
* functions.
|
||||||
|
* Level 2 (muslmimic): XSI — isascii, toascii.
|
||||||
|
*
|
||||||
|
* This header includes <vlibc/features.h> itself, so the gates below always
|
||||||
|
* see the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||||
|
* header first. The classification functions return exactly 1 when the
|
||||||
|
* character is in the class and 0 otherwise, never any other nonzero value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vlibc/features.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Level 1: ISO C (always present). */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is alphanumeric: a letter or a decimal digit.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isalnum(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a letter.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isalpha(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is blank: space or horizontal tab.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isblank(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a control character (0x00..0x1f or 0x7f).
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
iscntrl(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a decimal digit.
|
||||||
|
* const: bit arithmetic on the argument alone (also matches GCC's const
|
||||||
|
* builtin declaration for isdigit).
|
||||||
|
*/
|
||||||
|
__attribute__((const)) int
|
||||||
|
isdigit(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c has a visible representation (printable, except space).
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isgraph(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a lowercase letter.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
islower(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is printable (0x20..0x7e).
|
||||||
|
* pure: the implementation is bit arithmetic on the argument alone, but
|
||||||
|
* GCC's isprint builtin is declared pure, and a const redeclaration would
|
||||||
|
* conflict with it.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isprint(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a punctuation character: printable and not alphanumeric
|
||||||
|
* and not space.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
ispunct(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is white space (space, form feed, newline, carriage
|
||||||
|
* return, horizontal or vertical tab).
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isspace(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is an uppercase letter.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
isupper(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a hexadecimal digit (0-9, a-f, A-F).
|
||||||
|
* const: bit arithmetic on the argument alone (also matches GCC's const
|
||||||
|
* builtin declaration for isxdigit).
|
||||||
|
*/
|
||||||
|
__attribute__((const)) int
|
||||||
|
isxdigit(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the lowercase counterpart of c when c is an uppercase letter
|
||||||
|
* ('A'..'Z' in the "C" locale); otherwise return c unchanged, including
|
||||||
|
* EOF.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
tolower(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the uppercase counterpart of c when c is a lowercase letter
|
||||||
|
* ('a'..'z' in the "C" locale); otherwise return c unchanged, including
|
||||||
|
* EOF.
|
||||||
|
* pure: reads the classification table, no side effects.
|
||||||
|
*/
|
||||||
|
__attribute__((pure)) int
|
||||||
|
toupper(int c);
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
/* Level 2 (muslmimic): XSI. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nonzero when c is a 7-bit ASCII value (0..127). EOF and negative values
|
||||||
|
* are outside the range.
|
||||||
|
* const: depends only on its argument.
|
||||||
|
*/
|
||||||
|
__attribute__((const)) int
|
||||||
|
isascii(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear every bit above the low 7 of c (c & 0x7f).
|
||||||
|
* const: depends only on its argument.
|
||||||
|
*/
|
||||||
|
__attribute__((const)) int
|
||||||
|
toascii(int c);
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VLIBC_CTYPE_H */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* vlibc — ctype.h classification + case mapping test (todo 9).
|
||||||
|
*
|
||||||
|
* Coverage:
|
||||||
|
*
|
||||||
|
* 1. classification sweep: every is* function, for all 256 unsigned char
|
||||||
|
* values AND EOF, must agree with the "C" locale ASCII ranges the
|
||||||
|
* standard specifies — the golden model below derives its expectation
|
||||||
|
* straight from those ranges, independent of the implementation's
|
||||||
|
* table;
|
||||||
|
* 2. case-mapping sweep: tolower/toupper over all 256 values + EOF (map
|
||||||
|
* A-Z/a-z, pass every non-letter through unchanged, EOF -> EOF);
|
||||||
|
* 3. negative-char sweep: every negative signed-char value passed as int
|
||||||
|
* must classify false in all is* functions, case-map to itself, and
|
||||||
|
* never fault — proving the unsigned char cast keeps negative values
|
||||||
|
* from indexing out of bounds (isalpha((char)0xe9) == 0 included);
|
||||||
|
* 4. spot checks from the todo acceptance list;
|
||||||
|
* 5. -f: the negative QA — isprint('\n') == 0, isalpha(EOF) == 0,
|
||||||
|
* tolower(EOF)/toupper(EOF) == EOF (a nonzero isprint('\n') is the
|
||||||
|
* defect).
|
||||||
|
*
|
||||||
|
* No host headers: <ctype.h> is vlibc's own (-Iinclude shadows the system
|
||||||
|
* one), and every diagnostic goes through the raw SYS_write helpers from
|
||||||
|
* <vlibc/internal/test.h>. Compiled with -DVLIBC_LEVEL=2 so both the L1
|
||||||
|
* functions and the L2 isascii/toascii gate are exercised (mirrors
|
||||||
|
* libvlibc-check.a, which builds the level-2 surface).
|
||||||
|
*
|
||||||
|
* Not part of the library proper; compiled manually for this todo (the
|
||||||
|
* tests/ + make check wiring is owned by a later todo).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <vlibc/internal/test.h>
|
||||||
|
|
||||||
|
/* EOF as the standard defines it; ctype.h deliberately does not provide it. */
|
||||||
|
#define TEST_EOF (-1)
|
||||||
|
|
||||||
|
static int failures;
|
||||||
|
|
||||||
|
/* ---- Golden model: the "C" locale classes, as the standard defines them ---- */
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CLS_ALNUM,
|
||||||
|
CLS_ALPHA,
|
||||||
|
CLS_BLANK,
|
||||||
|
CLS_CNTRL,
|
||||||
|
CLS_DIGIT,
|
||||||
|
CLS_GRAPH,
|
||||||
|
CLS_LOWER,
|
||||||
|
CLS_PRINT,
|
||||||
|
CLS_PUNCT,
|
||||||
|
CLS_SPACE,
|
||||||
|
CLS_UPPER,
|
||||||
|
CLS_XDIGIT
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*ctype_pred)(int);
|
||||||
|
|
||||||
|
/* The real functions under test, indexed by the CLS_* enum above. */
|
||||||
|
static const ctype_pred cls_preds[] = {isalnum, isalpha, isblank, iscntrl, isdigit, isgraph,
|
||||||
|
islower, isprint, ispunct, isspace, isupper, isxdigit};
|
||||||
|
|
||||||
|
static const char *const cls_names[] = {"isalnum", "isalpha", "isblank", "iscntrl",
|
||||||
|
"isdigit", "isgraph", "islower", "isprint",
|
||||||
|
"ispunct", "isspace", "isupper", "isxdigit"};
|
||||||
|
|
||||||
|
static int
|
||||||
|
expect_class(int c, int cls) // NOLINT(bugprone-easily-swappable-parameters)
|
||||||
|
{
|
||||||
|
if (c < 0 || c > 127)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTBEGIN(bugprone-switch-missing-default-case)
|
||||||
|
switch (cls)
|
||||||
|
{
|
||||||
|
case CLS_UPPER:
|
||||||
|
return c >= 'A' && c <= 'Z';
|
||||||
|
case CLS_LOWER:
|
||||||
|
return c >= 'a' && c <= 'z';
|
||||||
|
case CLS_DIGIT:
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
case CLS_ALPHA:
|
||||||
|
return expect_class(c, CLS_UPPER) || expect_class(c, CLS_LOWER);
|
||||||
|
case CLS_ALNUM:
|
||||||
|
return expect_class(c, CLS_ALPHA) || expect_class(c, CLS_DIGIT);
|
||||||
|
case CLS_XDIGIT:
|
||||||
|
return expect_class(c, CLS_DIGIT) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||||
|
case CLS_CNTRL:
|
||||||
|
return (c >= 0x00 && c <= 0x1f) || c == 0x7f;
|
||||||
|
case CLS_BLANK:
|
||||||
|
return c == ' ' || c == '\t';
|
||||||
|
case CLS_SPACE:
|
||||||
|
return c == ' ' || (c >= '\t' && c <= '\r');
|
||||||
|
case CLS_PUNCT:
|
||||||
|
return (c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') ||
|
||||||
|
(c >= '{' && c <= '~');
|
||||||
|
case CLS_GRAPH:
|
||||||
|
return expect_class(c, CLS_ALNUM) || expect_class(c, CLS_PUNCT);
|
||||||
|
case CLS_PRINT:
|
||||||
|
return expect_class(c, CLS_GRAPH) || c == ' ';
|
||||||
|
}
|
||||||
|
// NOLINTEND(bugprone-switch-missing-default-case)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
expect_tolower(int c)
|
||||||
|
{
|
||||||
|
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
expect_toupper(int c)
|
||||||
|
{
|
||||||
|
return c >= 'a' && c <= 'z' ? c - ('a' - 'A') : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Sweeps ---- */
|
||||||
|
|
||||||
|
/* 1. All 256 byte values + EOF against the golden model, per class. */
|
||||||
|
static void
|
||||||
|
classification_sweep(void)
|
||||||
|
{
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
for (k = 0; k < sizeof(cls_preds) / sizeof(cls_preds[0]); k++)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
for (c = 0; c < 256; c++)
|
||||||
|
{
|
||||||
|
if ((cls_preds[k](c) != 0) == (expect_class(c, (int)k) != 0))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
vlibc_test_say(2, "mismatch: ");
|
||||||
|
vlibc_test_say(2, cls_names[k]);
|
||||||
|
vlibc_test_say(2, "(0x");
|
||||||
|
vlibc_test_say_hex(2, (unsigned long long)c);
|
||||||
|
vlibc_test_say(2, ")\n");
|
||||||
|
failures++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cls_preds[k](TEST_EOF) != 0)
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "mismatch: ");
|
||||||
|
vlibc_test_say(2, cls_names[k]);
|
||||||
|
vlibc_test_say(2, "(EOF)\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Case mapping over all 256 byte values + EOF. */
|
||||||
|
static void
|
||||||
|
case_mapping_sweep(void)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
for (c = 0; c < 256; c++)
|
||||||
|
{
|
||||||
|
if (tolower(c) != expect_tolower(c))
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "tolower mismatch at 0x");
|
||||||
|
vlibc_test_say_hex(2, (unsigned long long)c);
|
||||||
|
vlibc_test_say(2, "\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
if (toupper(c) != expect_toupper(c))
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "toupper mismatch at 0x");
|
||||||
|
vlibc_test_say_hex(2, (unsigned long long)c);
|
||||||
|
vlibc_test_say(2, "\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tolower(TEST_EOF) != TEST_EOF || toupper(TEST_EOF) != TEST_EOF)
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "case mapping of EOF did not return EOF unchanged\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. Every negative signed-char value: classify false, case-map to itself. */
|
||||||
|
static void
|
||||||
|
negative_char_sweep(void)
|
||||||
|
{
|
||||||
|
signed char sc;
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
for (sc = (signed char)-128; (int)sc < 0; sc++)
|
||||||
|
{
|
||||||
|
int c = (int)sc;
|
||||||
|
|
||||||
|
for (k = 0; k < sizeof(cls_preds) / sizeof(cls_preds[0]); k++)
|
||||||
|
{
|
||||||
|
if (cls_preds[k](c) != 0)
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "negative value classified nonzero by ");
|
||||||
|
vlibc_test_say(2, cls_names[k]);
|
||||||
|
vlibc_test_say(2, "\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tolower(c) != c || toupper(c) != c)
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "negative value case-mapped instead of passing through\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
if (isascii(c) != 0)
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "negative value classified ASCII by isascii\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
if (toascii(c) != (c & 0x7f))
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "toascii mismatch on a negative value\n");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. Acceptance-list spot checks. */
|
||||||
|
static void
|
||||||
|
spot_checks(void)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQ(tolower('A'), 'a');
|
||||||
|
TEST_ASSERT_EQ(toupper('a'), 'A');
|
||||||
|
TEST_ASSERT_EQ(tolower('a'), 'a'); /* lowercase identity */
|
||||||
|
TEST_ASSERT_EQ(toupper('Z'), 'Z'); /* uppercase identity */
|
||||||
|
TEST_ASSERT_EQ(tolower('Z'), 'z');
|
||||||
|
TEST_ASSERT_EQ(toupper('z'), 'Z');
|
||||||
|
TEST_ASSERT_EQ(tolower('0'), '0'); /* digit identity */
|
||||||
|
TEST_ASSERT_TRUE(isupper('A'));
|
||||||
|
TEST_ASSERT_TRUE(islower('a'));
|
||||||
|
TEST_ASSERT_TRUE(isalpha(TEST_EOF) == 0);
|
||||||
|
TEST_ASSERT_TRUE(isprint('\n') == 0); /* negative QA: no false positive */
|
||||||
|
TEST_ASSERT_TRUE(isdigit('5'));
|
||||||
|
TEST_ASSERT_TRUE(!isalpha('5'));
|
||||||
|
TEST_ASSERT_TRUE(isspace(' '));
|
||||||
|
TEST_ASSERT_TRUE(isspace('\n'));
|
||||||
|
TEST_ASSERT_TRUE(ispunct('!'));
|
||||||
|
TEST_ASSERT_TRUE(!ispunct('A'));
|
||||||
|
TEST_ASSERT_TRUE(isgraph('~'));
|
||||||
|
TEST_ASSERT_TRUE(!isgraph(' '));
|
||||||
|
TEST_ASSERT_TRUE(isprint(' '));
|
||||||
|
TEST_ASSERT_TRUE(isblank('\t'));
|
||||||
|
TEST_ASSERT_TRUE(isblank(' '));
|
||||||
|
TEST_ASSERT_TRUE(!isblank('x'));
|
||||||
|
TEST_ASSERT_TRUE(iscntrl(0x7f));
|
||||||
|
TEST_ASSERT_TRUE(!iscntrl(' '));
|
||||||
|
TEST_ASSERT_TRUE(isxdigit('f'));
|
||||||
|
TEST_ASSERT_TRUE(isxdigit('F'));
|
||||||
|
TEST_ASSERT_TRUE(!isxdigit('g'));
|
||||||
|
TEST_ASSERT_TRUE(!isalnum('@'));
|
||||||
|
TEST_ASSERT_TRUE(isalnum('A'));
|
||||||
|
TEST_ASSERT_TRUE(isalpha((char)0xe9) == 0); /* negative char: (char)0xe9 == -23 */
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
TEST_ASSERT_TRUE(!isascii(0x80));
|
||||||
|
TEST_ASSERT_TRUE(isascii(0x00));
|
||||||
|
TEST_ASSERT_TRUE(isascii(0x7f));
|
||||||
|
TEST_ASSERT_TRUE(!isascii(TEST_EOF));
|
||||||
|
TEST_ASSERT_EQ(toascii('A'), 'A'); /* c & 0x7f, not a predicate */
|
||||||
|
TEST_ASSERT_EQ(toascii(0x80), 0);
|
||||||
|
TEST_ASSERT_EQ(toascii(TEST_EOF), 0x7f);
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Failure scenario (-f): the negative QA from the todo. Exits 0 only when
|
||||||
|
* each defect is absent.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
failure_scenario(void)
|
||||||
|
{
|
||||||
|
if (isprint('\n') != 0)
|
||||||
|
{
|
||||||
|
vlibc_test_say(1, "isprint('\\n') != 0 (false positive)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (isalpha(TEST_EOF) != 0)
|
||||||
|
{
|
||||||
|
vlibc_test_say(1, "isalpha(EOF) != 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (tolower(TEST_EOF) != TEST_EOF || toupper(TEST_EOF) != TEST_EOF)
|
||||||
|
{
|
||||||
|
vlibc_test_say(1, "tolower/toupper(EOF) did not return EOF unchanged\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
vlibc_test_say(1, "negative QA: isprint('\\n') == 0, isalpha(EOF) == 0, "
|
||||||
|
"tolower/toupper(EOF) == EOF\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f')
|
||||||
|
{
|
||||||
|
return failure_scenario();
|
||||||
|
}
|
||||||
|
|
||||||
|
classification_sweep();
|
||||||
|
case_mapping_sweep();
|
||||||
|
negative_char_sweep();
|
||||||
|
spot_checks();
|
||||||
|
|
||||||
|
if (failures > 0 || vlibc_test_failures > 0)
|
||||||
|
{
|
||||||
|
vlibc_test_say(2, "FAILED (");
|
||||||
|
// NOLINTNEXTLINE(bugprone-misplaced-widening-cast)
|
||||||
|
vlibc_test_say_dec(2, (unsigned long)(failures + vlibc_test_failures));
|
||||||
|
vlibc_test_say(2, " check(s))\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
vlibc_test_say(1, "all ctype tests passed\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user