byte_array_suite.tc¶
byte_array_suite.tc — umfassende Testsuite für den byte[]-Modus (TinyC 1.6.57).
// @name: byte[] Test Suite
// byte_array_suite.tc — umfassende Testsuite für den byte[]-Modus (TinyC 1.6.57).
//
// Selbstprüfend: jeder Test vergleicht gegen den Sollwert und zählt PASS/FAIL.
// Läuft OHNE Extra-Hardware (nur Speicher/Krypto/Hex/Datei), also direkt am
// Gerät. Am Ende steht die Zeile: byte-suite: N/M PASSED
//
// Deckt ab: lokale Frame-byte[], lokale Heap-byte[], globale (Heap + Nicht-
// Heap), Packungsgrenzen/Nachbarschaft, byte[] als Referenz-Argument, byte[]
// als Text lesen (%s), die byte-orientierten Syscalls (sha256/md5/hmac/hex2bin/
// bin2hex/base64/aesEcb), Datei-I/O mit rohen Bytes, und `byte` als Typ-Alias.
//
// Seit 1.6.58 sind auch die String-SCHREIB-Syscalls byte-bewusst: strcpy/strcat/
// sprintf/strToUpper/strToLower/strTrim/strReplace/strSub/strToken schreiben in
// eine byte[] gepackt, und strlen/strcmp/strFind/strContains/starts/endsWith
// lesen sie gepackt. Eine byte[] ist damit ein voller Ersatz für char[].
int g_pass = 0;
int g_fail = 0;
void ck(char name[], int got, int exp) {
if (got == exp) {
g_pass = g_pass + 1;
} else {
g_fail = g_fail + 1;
addLog(" FAIL %s: got=%d exp=%d", name, got, exp);
}
}
// ── globale Speicherklassen ──────────────────────────────────────────────
byte gsmall[16]; // global, NICHT-Heap (16 Elemente = 4 Slots, in globals[])
byte gbig[4096]; // global, Heap (1024 Slots)
byte gdig[32]; // Krypto-Ausgabe (binär)
char ghex[80]; // Hex-TEXT: char[] — strcmp ist NICHT byte-bewusst
char chex[80]; // Vergleich char[]
// 1) lokales Frame-byte[] (<=16 Elemente): schreiben/lesen/Summe/Kappung
void test_local_frame() {
byte b[16];
int i = 0;
while (i < 16) { b[i] = i * 2; i = i + 1; } // 0,2,...,30
int s = 0; i = 0;
while (i < 16) { s = s + b[i]; i = i + 1; }
ck("frame[16] sum", s, 240);
ck("frame[16] last", b[15], 30);
b[0] = 300; // nur ein Byte -> 44
ck("frame trunc 300", b[0], 44);
b[1] = -1; // -1 & 0xFF -> 255
ck("frame trunc -1", b[1], 255);
}
// 2) lokales Heap-byte[] (>16 Elemente -> Heap): Packung + hohe Indizes
void test_local_heap() {
byte h[100];
int i = 0;
while (i < 100) { h[i] = (i * 7) & 255; i = i + 1; }
ck("heap[100][0]", h[0], 0);
ck("heap[100][50]", h[50], (50 * 7) & 255);
ck("heap[100][99]", h[99], (99 * 7) & 255);
}
// 3) Nachbarschaft/Packungsgrenzen: zwei byte[5] nebeneinander (je 2 Slots),
// Vollschreiben des einen darf das andere nicht verschmutzen
void test_adjacency() {
byte a5[5];
byte b5[5];
int i = 0;
while (i < 5) { a5[i] = 100 + i; i = i + 1; }
i = 0;
while (i < 5) { b5[i] = 200 + i; i = i + 1; }
ck("adj a5[0]", a5[0], 100);
ck("adj a5[4]", a5[4], 104);
ck("adj b5[0]", b5[0], 200);
ck("adj b5[4]", b5[4], 204);
}
// 4) globale byte[] (Nicht-Heap + Heap)
void test_global() {
gsmall[0] = 255;
gsmall[14] = 40;
gsmall[15] = 42; // letztes Byte eines globalen Nicht-Heap byte[]
ck("gsmall[0]", gsmall[0], 255);
ck("gsmall[14]", gsmall[14], 40);
ck("gsmall[15]", gsmall[15], 42);
gbig[0] = 111;
gbig[1023] = 222;
gbig[4095] = 99;
ck("gbig[0]", gbig[0], 111);
ck("gbig[1023]", gbig[1023], 222);
ck("gbig[4095]", gbig[4095], 99);
}
// 5) byte[] als Referenz-Argument (füllen + summieren über Funktionen)
void fill_bytes(byte buf[], int n, int base) {
int i = 0;
while (i < n) { buf[i] = (base + i) & 255; i = i + 1; }
}
int sum_bytes(byte buf[], int n) {
int s = 0;
int i = 0;
while (i < n) { s = s + buf[i]; i = i + 1; }
return s;
}
void test_refarg() {
byte b[16];
fill_bytes(b, 16, 10); // 10..25
ck("refarg read", b[5], 15);
ck("refarg sum", sum_bytes(b, 16), 280);
fill_bytes(gbig, 50, 1); // Heap-Array als Ref-Arg
ck("refarg heap sum", sum_bytes(gbig, 50), 1275);
}
// 6) byte[] als Text LESEN (%s) — der byte-bewusste Lesepfad
void test_text_read() {
byte tb[16];
tb[0] = 72; tb[1] = 105; tb[2] = 33; tb[3] = 0; // "Hi!"
char out[32];
sprintf(out, "[%s]", tb);
ck("byte[] via %s", strcmp(out, "[Hi!]"), 0);
}
// 7) byte-orientierte Syscalls
void test_syscalls() {
// hex2bin -> byte[]
int n = hex2bin("48656c6c6f21", 12, gbig); // "Hello!"
ck("hex2bin n", n, 6);
ck("hex2bin[0]", gbig[0], 72);
ck("hex2bin[5]", gbig[5], 33);
// bin2hex(byte[]) zurück
bin2hex(gbig, 6, ghex);
ck("bin2hex", strcmp(ghex, "48656c6c6f21"), 0);
// sha256("abc") aus byte[] gegen bekannten Vektor
gbig[0] = 97; gbig[1] = 98; gbig[2] = 99;
sha256(gbig, 3, gdig);
bin2hex(gdig, 32, ghex);
ck("sha256(abc)", strcmp(ghex,
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"), 0);
// md5("abc") aus byte[] gegen bekannten Vektor
gbig[0] = 97; gbig[1] = 98; gbig[2] = 99;
md5(gbig, 3, gdig);
bin2hex(gdig, 16, ghex);
ck("md5(abc)", strcmp(ghex, "900150983cd24fb0d6963f7d28e17f72"), 0);
// base64Enc("Hi!") aus byte[] -> "SGkh" (Ausgabe char[], strcmp braucht char[])
byte b64in[8];
b64in[0] = 72; b64in[1] = 105; b64in[2] = 33;
char b64out[16];
base64Enc(b64in, 3, b64out);
ck("base64(Hi!)", strcmp(b64out, "SGkh"), 0);
// hmacSha256: byte[]-Pfad muss dasselbe liefern wie der char[]-Pfad
byte bkey[8]; byte bdat[8]; byte bout[32];
char ckey[8]; char cdat[8]; char cout[32];
int i = 0;
while (i < 4) { bkey[i] = 65 + i; ckey[i] = 65 + i; i = i + 1; } // "ABCD"
i = 0;
while (i < 3) { bdat[i] = 120 + i; cdat[i] = 120 + i; i = i + 1; } // "xyz"
hmacSha256(bkey, 4, bdat, 3, bout);
hmacSha256(ckey, 4, cdat, 3, cout);
bin2hex(bout, 32, ghex);
bin2hex(cout, 32, chex);
ck("hmac byte==char", strcmp(ghex, chex), 0);
// aesEcb: Schlüssel+Daten als byte[], Roundtrip
byte key[16];
byte blk[16];
i = 0;
while (i < 16) { key[i] = i; blk[i] = 100 + i; i = i + 1; }
int klar = blk[0];
aesEcb(key, blk, 1);
int enc = blk[0];
aesEcb(key, blk, 0);
ck("aes enc changed", (enc != klar), 1);
ck("aes roundtrip", blk[0], klar);
}
// 8) Datei-I/O mit ROHEN Bytes (byte[] -> 1 Byte je Element, keine 4-Byte-LE)
void test_file() {
byte wb[64];
int i = 0;
while (i < 64) { wb[i] = (i * 3) & 255; i = i + 1; }
int fh = fileOpen("/bsuite.bin", "w");
int wn = fileWriteBin(fh, wb, 64);
fileClose(fh);
ck("fileWriteBin bytes", wn, 64);
byte rb[64];
int fh2 = fileOpen("/bsuite.bin", "r");
int rn = fileReadBin(fh2, rb, 64);
fileClose(fh2);
ck("fileReadBin bytes", rn, 64);
int ok = 1;
i = 0;
while (i < 64) { if (rb[i] != ((i * 3) & 255)) { ok = 0; } i = i + 1; }
ck("file roundtrip", ok, 1);
fileDelete("/bsuite.bin");
}
// 9) `byte` als Typ-Alias, nicht als Schlüsselwort: als Variablenname nutzbar
void test_alias() {
int byte = 5;
byte = byte + 3;
ck("byte as varname", byte, 8);
}
// 10) String-SCHREIB-Syscalls auf einer byte[] (byte-bewusst seit 1.6.58)
void test_string_ops() {
byte s[40];
strcpy(s, "Hallo"); // STRCPY_CONST
ck("strcpy/strlen", strlen(s), 5);
ck("strcpy/strcmp", strcmp(s, "Hallo"), 0);
strcat(s, " Welt"); // STRCAT_CONST
ck("strcat", strcmp(s, "Hallo Welt"), 0);
sprintf(s, "n=%d s=%s", 7, "ok"); // SPRINTF_INT/STR
ck("sprintf", strcmp(s, "n=7 s=ok"), 0);
strcpy(s, "MixCase");
strToUpper(s);
ck("strToUpper", strcmp(s, "MIXCASE"), 0);
strToLower(s);
ck("strToLower", strcmp(s, "mixcase"), 0);
strcpy(s, " hi ");
ck("strTrim ret", strTrim(s), 2);
ck("strTrim val", strcmp(s, "hi"), 0);
strcpy(s, "a.b.c");
ck("strReplace cnt", strReplace(s, ".", "-"), 2);
ck("strReplace val", strcmp(s, "a-b-c"), 0);
strcpy(s, "foobar");
ck("startsWith", strStartsWith(s, "foo"), 1);
ck("endsWith", strEndsWith(s, "bar"), 1);
ck("contains", strContains(s, "oob"), 1);
ck("strFind", strFind(s, "bar"), 3);
byte sub[16];
strSub(sub, s, 1, 3); // "oob"
ck("strSub", strcmp(sub, "oob"), 0);
byte tk[16];
strcpy(s, "x,y,z");
strToken(tk, s, ',', 2); // "y"
ck("strToken", strcmp(tk, "y"), 0);
// byte[] nach String-Schreibern als %s lesen + strcpy byte[]->char[]
strcpy(s, "Read");
char out[40];
sprintf(out, "<%s>", s);
ck("byte[] as %s", strcmp(out, "<Read>"), 0);
char cd[16];
strcpy(cd, s); // src byte[] -> dst char[]
ck("strcpy byte->char", strcmp(cd, "Read"), 0);
}
// 11) `byte`/`uint8_t` in structs — gepackte Felder, Nachbarfeld intakt
struct Small { byte b[8]; int x; }; // 2+1 Slots -> Nicht-Heap
struct Pkt { byte payload[64]; int len; }; // 16+1 Slots -> Heap
Pkt gpkt;
Small sarr[3];
void test_struct_byte() {
Small s;
int i = 0;
while (i < 8) { s.b[i] = (i * 11) & 255; i = i + 1; }
s.x = 12345;
int ok = 1; i = 0;
while (i < 8) { if (s.b[i] != ((i * 11) & 255)) { ok = 0; } i = i + 1; }
ck("struct local byte field", ok, 1);
ck("struct local neighbor", s.x, 12345); // Packung ok -> nicht verschmutzt
// Heap-Struct (>16 Slots) mit byte[]-Feld
i = 0;
while (i < 64) { gpkt.payload[i] = (i * 7) & 255; i = i + 1; }
gpkt.len = 64;
ck("struct heap payload[63]", gpkt.payload[63], (63 * 7) & 255);
ck("struct heap len", gpkt.len, 64);
// Struct-Array mit byte[]-Feld
sarr[0].b[0] = 10; sarr[0].x = 100;
sarr[1].b[0] = 20; sarr[1].x = 200;
sarr[2].b[7] = 37; sarr[2].x = 300;
ck("sarr[1].b[0]", sarr[1].b[0], 20);
ck("sarr[2].b[7]", sarr[2].b[7], 37);
ck("sarr[1].x intact", sarr[1].x, 200);
}
// 12) `uint8_t` ist ein Alias für `byte` (gepackt, an byte-Funktionen übergebbar)
void test_uint8() {
uint8_t u[16];
int i = 0;
while (i < 16) { u[i] = (i * 15) & 255; i = i + 1; }
ck("uint8_t[15]", u[15], (15 * 15) & 255);
ck("uint8_t as byte-arg", sum_bytes(u, 16), 1800); // uint8_t == byte
}
int main() {
addLog("byte-suite: start");
test_local_frame();
test_local_heap();
test_adjacency();
test_global();
test_refarg();
test_text_read();
test_syscalls();
test_file();
test_alias();
test_string_ops();
test_struct_byte();
test_uint8();
int total = g_pass + g_fail;
addLog("byte-suite: %d/%d PASSED%s", g_pass, total,
(g_fail == 0) ? " ✅ ALL GOOD" : " ⚠️ FAILURES ABOVE");
return 0;
}