Compare commits

...

9 commits

Author SHA1 Message Date
erykwieliczko 9222392ff3
Merge 59c7ea08c2 into 130fd66f9e 2024-05-03 17:53:54 +00:00
Justine Tunney 130fd66f9e
Release Cosmopolitan v3.3.4 2024-05-03 09:25:02 -07:00
Justine Tunney 8a44f913ae
Delete flaky tests
Signals are extremely difficult to unit test reliably. This is why
functions like sigsuspend() exist. When testing something else and
portably it becomes impossible without access to kernel internals.

OpenMP flakes in QEMU on one of my workstations. I don't think the
support is production worthy, because there's been issues on MacOS
additionally. It works great for every experiment I've used it for
though. However a flaky test is worse than no test at all. So it's
removed until someone takes an interest in productionizing it.
2024-05-03 09:11:04 -07:00
Gautham 5488f0b2ca
Remove zlib namespacing (#1142)
We have an optimized version of zlib from the Chromium project.
We need it for a lot of our libc services. It would be nice to export
this to user applications if we can, since projects like llamafile are
already depending on it under the private namespace, to avoid
needing to link zlib twice.
2024-05-03 08:07:25 -07:00
Gavin Hayes deff138e7e
recvfrom: don't convert address if addrsize is 0 (#1153) 2024-05-03 08:03:57 -07:00
Gavin Hayes b6e40a3a58
Add /dev/(u)random on NT (#1163) 2024-05-03 07:59:51 -07:00
Cadence Ember 8f6bc9dabc
Let signals interrupt fgets unless SA_RESTART set (#1152) 2024-05-03 07:49:41 -07:00
Justine Tunney 181cd4cbe8
Add sysctlbyname() for MacOS 2024-05-02 23:21:43 -07:00
Eryk Wieliczko 59c7ea08c2 Added Hunt the Wumpus to examples 2024-04-02 21:41:07 +02:00
52 changed files with 1204 additions and 561 deletions

View file

@ -31,6 +31,8 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
@ -39,7 +41,7 @@
/* maximum path size that cosmo can take */
#define PATHSIZE (PATH_MAX < 1024 ? PATH_MAX : 1024)
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
#define SYSLIB_VERSION 9 /* sync with libc/runtime/syslib.internal.h */
#define SYSLIB_VERSION 10 /* sync with libc/runtime/syslib.internal.h */
struct Syslib {
int magic;
@ -106,6 +108,10 @@ struct Syslib {
OPTIONAL (cosmo lib should check __syslib->version) */
/* v9 (2024-01-31) */
int (*pthread_cpu_number_np)(size_t *);
/* v10 (2024-05-02) */
long (*sysctl)(int *, u_int, void *, size_t *, void *, size_t);
long (*sysctlbyname)(const char *, void *, size_t *, void *, size_t);
long (*sysctlnametomib)(const char *, int *, size_t *);
};
#define ELFCLASS32 1
@ -148,8 +154,8 @@ struct Syslib {
#define _COMM_PAGE_APRR_WRITE_ENABLE (_COMM_PAGE_START_ADDRESS + 0x110)
#define _COMM_PAGE_APRR_WRITE_DISABLE (_COMM_PAGE_START_ADDRESS + 0x118)
#define MIN(X, Y) ((Y) > (X) ? (X) : (Y))
#define MAX(X, Y) ((Y) < (X) ? (X) : (Y))
#define Min(X, Y) ((Y) > (X) ? (X) : (Y))
#define Max(X, Y) ((Y) < (X) ? (X) : (Y))
#define READ32(S) \
((unsigned)(255 & (S)[3]) << 030 | (unsigned)(255 & (S)[2]) << 020 | \
@ -552,6 +558,20 @@ static long sys_pselect(int nfds, fd_set *readfds, fd_set *writefds,
return sysret(pselect(nfds, readfds, writefds, errorfds, timeout, sigmask));
}
static long sys_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen) {
return sysret(sysctl(name, namelen, oldp, oldlenp, newp, newlen));
}
static long sys_sysctlbyname(const char *name, void *oldp, size_t *oldlenp,
void *newp, size_t newlen) {
return sysret(sysctlbyname(name, oldp, oldlenp, newp, newlen));
}
static long sys_sysctlnametomib(const char *name, int *mibp, size_t *sizep) {
return sysret(sysctlnametomib(name, mibp, sizep));
}
__attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
long *sp, struct ElfEhdr *e,
struct ElfPhdr *p,
@ -596,7 +616,7 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
continue;
c = p[j].p_vaddr & -pagesz;
d = (p[j].p_vaddr + p[j].p_memsz + (pagesz - 1)) & -pagesz;
if (MAX(a, c) < MIN(b, d)) {
if (Max(a, c) < Min(b, d)) {
Pexit(exe, 0, "ELF segments overlap each others virtual memory");
}
}
@ -670,7 +690,7 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
a = p[i].p_vaddr + p[i].p_filesz; /* end of file content */
b = (a + (pagesz - 1)) & -pagesz; /* first pure bss page */
c = p[i].p_vaddr + p[i].p_memsz; /* end of segment data */
wipe = MIN(b - a, c - a);
wipe = Min(b - a, c - a);
if (wipe && (~prot1 & PROT_WRITE)) {
prot1 = PROT_READ | PROT_WRITE;
}
@ -970,6 +990,9 @@ int main(int argc, char **argv, char **envp) {
M->lib.dlclose = dlclose;
M->lib.dlerror = dlerror;
M->lib.pthread_cpu_number_np = pthread_cpu_number_np;
M->lib.sysctl = sys_sysctl;
M->lib.sysctlbyname = sys_sysctlbyname;
M->lib.sysctlnametomib = sys_sysctlnametomib;
/* getenv("_") is close enough to at_execfn */
execfn = 0;

838
examples/wump.c Normal file
View file

@ -0,0 +1,838 @@
/* $NetBSD: wump.c,v 1.17 2005/02/15 12:56:20 jsm Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Dave Taylor, of Intuitive Systems.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)wump.c 8.1 (Berkeley) 5/31/93";
#else
#endif
#endif /* not lint */
#define u_int uint32_t
/*
* A very new version of the age old favorite Hunt-The-Wumpus game that has
* been a part of the BSD distribution of Unix for longer than us old folk
* would care to remember.
*/
#include <err.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* some defines to spec out what our wumpus cave should look like */
#define MAX_ARROW_SHOT_DISTANCE 6 /* +1 for '0' stopper */
#define MAX_LINKS_IN_ROOM 25 /* a complex cave */
#define MAX_ROOMS_IN_CAVE 250
#define ROOMS_IN_CAVE 20
#define MIN_ROOMS_IN_CAVE 10
#define LINKS_IN_ROOM 3
#define NUMBER_OF_ARROWS 5
#define PIT_COUNT 3
#define BAT_COUNT 3
#define EASY 1 /* levels of play */
#define HARD 2
/* some macro definitions for cleaner output */
#define plural(n) (n == 1 ? "" : "s")
/* simple cave data structure; +1 so we can index from '1' not '0' */
struct room_record {
int tunnel[MAX_LINKS_IN_ROOM];
int has_a_pit, has_a_bat;
} cave[MAX_ROOMS_IN_CAVE+1];
/*
* global variables so we can keep track of where the player is, how
* many arrows they still have, where el wumpo is, and so on...
*/
int player_loc = -1; /* player location */
int wumpus_loc = -1; /* The Bad Guy location */
int level = EASY; /* level of play */
int arrows_left; /* arrows unshot */
#ifdef DEBUG
int debug = 0;
#endif
int pit_num = PIT_COUNT; /* # pits in cave */
int bat_num = BAT_COUNT; /* # bats */
int room_num = ROOMS_IN_CAVE; /* # rooms in cave */
int link_num = LINKS_IN_ROOM; /* links per room */
int arrow_num = NUMBER_OF_ARROWS; /* arrow inventory */
char answer[20]; /* user input */
int bats_nearby(void);
void cave_init(void);
void clear_things_in_cave(void);
void display_room_stats(void);
int gcd(int, int);
int getans(const char *);
void initialize_things_in_cave(void);
void instructions(void);
int int_compare(const void *, const void *);
void jump(int);
void kill_wump(void);
int main(int, char **);
int move_to(const char *);
void move_wump(void);
void no_arrows(void);
void pit_kill(void);
int pit_nearby(void);
void pit_survive(void);
int shoot(char *);
void shoot_self(void);
int take_action(void);
void usage(void) __attribute__((__noreturn__));
void wump_kill(void);
int wump_nearby(void);
int
main(argc, argv)
int argc;
char **argv;
{
int c;
/* Revoke setgid privileges */
setregid(getgid(), getgid());
#ifdef DEBUG
while ((c = getopt(argc, argv, "a:b:hp:r:t:d")) != -1)
#else
while ((c = getopt(argc, argv, "a:b:hp:r:t:")) != -1)
#endif
switch (c) {
case 'a':
arrow_num = atoi(optarg);
break;
case 'b':
bat_num = atoi(optarg);
break;
#ifdef DEBUG
case 'd':
debug = 1;
break;
#endif
case 'h':
level = HARD;
break;
case 'p':
pit_num = atoi(optarg);
break;
case 'r':
room_num = atoi(optarg);
if (room_num < MIN_ROOMS_IN_CAVE) {
(void)fprintf(stderr,
"No self-respecting wumpus would live in such a small cave!\n");
exit(1);
}
if (room_num > MAX_ROOMS_IN_CAVE) {
(void)fprintf(stderr,
"Even wumpii can't furnish caves that large!\n");
exit(1);
}
break;
case 't':
link_num = atoi(optarg);
if (link_num < 2) {
(void)fprintf(stderr,
"Wumpii like extra doors in their caves!\n");
exit(1);
}
break;
case '?':
default:
usage();
}
if (link_num > MAX_LINKS_IN_ROOM ||
link_num > room_num - (room_num / 4)) {
(void)fprintf(stderr,
"Too many tunnels! The cave collapsed!\n(Fortunately, the wumpus escaped!)\n");
exit(1);
}
if (level == HARD) {
bat_num += ((random() % (room_num / 2)) + 1);
pit_num += ((random() % (room_num / 2)) + 1);
}
if (bat_num > room_num / 2) {
(void)fprintf(stderr,
"The wumpus refused to enter the cave, claiming it was too crowded!\n");
exit(1);
}
if (pit_num > room_num / 2) {
(void)fprintf(stderr,
"The wumpus refused to enter the cave, claiming it was too dangerous!\n");
exit(1);
}
instructions();
cave_init();
/* and we're OFF! da dum, da dum, da dum, da dum... */
(void)printf(
"\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\
There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
quiver holds %d custom super anti-evil Wumpus arrows. Good luck.\n",
room_num, link_num, bat_num, plural(bat_num), pit_num,
plural(pit_num), arrow_num);
for (;;) {
initialize_things_in_cave();
arrows_left = arrow_num;
do {
display_room_stats();
(void)printf("Move or shoot? (m-s) ");
(void)fflush(stdout);
if (!fgets(answer, sizeof(answer), stdin))
break;
} while (!take_action());
if (!getans("\nCare to play another game? (y-n) "))
exit(0);
if (getans("In the same cave? (y-n) "))
clear_things_in_cave();
else
cave_init();
}
/* NOTREACHED */
return (0);
}
void
display_room_stats()
{
int i;
/*
* Routine will explain what's going on with the current room, as well
* as describe whether there are pits, bats, & wumpii nearby. It's
* all pretty mindless, really.
*/
(void)printf(
"\nYou are in room %d of the cave, and have %d arrow%s left.\n",
player_loc, arrows_left, plural(arrows_left));
if (bats_nearby())
(void)printf("*rustle* *rustle* (must be bats nearby)\n");
if (pit_nearby())
(void)printf("*whoosh* (I feel a draft from some pits).\n");
if (wump_nearby())
(void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n");
(void)printf("There are tunnels to rooms %d, ",
cave[player_loc].tunnel[0]);
for (i = 1; i < link_num - 1; i++)
if (cave[player_loc].tunnel[i] <= room_num)
(void)printf("%d, ", cave[player_loc].tunnel[i]);
(void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
}
int
take_action()
{
/*
* Do the action specified by the player, either 'm'ove, 's'hoot
* or something exceptionally bizarre and strange! Returns 1
* iff the player died during this turn, otherwise returns 0.
*/
switch (*answer) {
case 'M':
case 'm': /* move */
return(move_to(answer + 1));
case 'S':
case 's': /* shoot */
return(shoot(answer + 1));
case 'Q':
case 'q':
case 'x':
exit(0);
case '\n':
return(0);
}
if (random() % 15 == 1)
(void)printf("Que pasa?\n");
else
(void)printf("I don't understand!\n");
return(0);
}
int
move_to(room_number)
const char *room_number;
{
int i, just_moved_by_bats, next_room, tunnel_available;
/*
* This is responsible for moving the player into another room in the
* cave as per their directions. If room_number is a null string,
* then we'll prompt the user for the next room to go into. Once
* we've moved into the room, we'll check for things like bats, pits,
* and so on. This routine returns 1 if something occurs that kills
* the player and 0 otherwise...
*/
tunnel_available = just_moved_by_bats = 0;
next_room = atoi(room_number);
/* crap for magic tunnels */
if (next_room == room_num + 1 &&
cave[player_loc].tunnel[link_num-1] != next_room)
++next_room;
while (next_room < 1 || next_room > room_num + 1) {
if (next_room < 0 && next_room != -1)
(void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n");
if (next_room > room_num + 1)
(void)printf("What? The cave surely isn't quite that big!\n");
if (next_room == room_num + 1 &&
cave[player_loc].tunnel[link_num-1] != next_room) {
(void)printf("What? The cave isn't that big!\n");
++next_room;
}
(void)printf("To which room do you wish to move? ");
(void)fflush(stdout);
if (!fgets(answer, sizeof(answer), stdin))
return(1);
next_room = atoi(answer);
}
/* now let's see if we can move to that room or not */
tunnel_available = 0;
for (i = 0; i < link_num; i++)
if (cave[player_loc].tunnel[i] == next_room)
tunnel_available = 1;
if (!tunnel_available) {
(void)printf("*Oof!* (You hit the wall)\n");
if (random() % 6 == 1) {
(void)printf("Your colorful comments awaken the wumpus!\n");
move_wump();
if (wumpus_loc == player_loc) {
wump_kill();
return(1);
}
}
return(0);
}
/* now let's move into that room and check it out for dangers */
if (next_room == room_num + 1)
jump(next_room = (random() % room_num) + 1);
player_loc = next_room;
for (;;) {
if (next_room == wumpus_loc) { /* uh oh... */
wump_kill();
return(1);
}
if (cave[next_room].has_a_pit) {
if (random() % 12 < 2) {
pit_survive();
return(0);
} else {
pit_kill();
return(1);
}
}
if (cave[next_room].has_a_bat) {
(void)printf(
"*flap* *flap* *flap* (humongous bats pick you up and move you%s!)\n",
just_moved_by_bats ? " again": "");
next_room = player_loc = (random() % room_num) + 1;
just_moved_by_bats = 1;
}
else
break;
}
return(0);
}
int
shoot(room_list)
char *room_list;
{
int chance, next, roomcnt;
int j, arrow_location, link, ok;
char *p;
/*
* Implement shooting arrows. Arrows are shot by the player indicating
* a space-separated list of rooms that the arrow should pass through;
* if any of the rooms they specify are not accessible via tunnel from
* the room the arrow is in, it will instead fly randomly into another
* room. If the player hits the wumpus, this routine will indicate
* such. If it misses, this routine will *move* the wumpus one room.
* If it's the last arrow, the player then dies... Returns 1 if the
* player has won or died, 0 if nothing has happened.
*/
arrow_location = player_loc;
for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
if (!(p = strtok(room_list, " \t\n"))) {
if (roomcnt == 1) {
(void)printf(
"The arrow falls to the ground at your feet!\n");
return(0);
} else
break;
}
if (roomcnt > 5) {
(void)printf(
"The arrow wavers in its flight and and can go no further!\n");
break;
}
next = atoi(p);
for (j = 0, ok = 0; j < link_num; j++)
if (cave[arrow_location].tunnel[j] == next)
ok = 1;
if (ok) {
if (next > room_num) {
(void)printf(
"A faint gleam tells you the arrow has gone through a magic tunnel!\n");
arrow_location = (random() % room_num) + 1;
} else
arrow_location = next;
} else {
link = (random() % link_num);
if (link == player_loc)
(void)printf(
"*thunk* The arrow can't find a way from %d to %d and flys back into\n\
your room!\n",
arrow_location, next);
else if (cave[arrow_location].tunnel[link] > room_num)
(void)printf(
"*thunk* The arrow flys randomly into a magic tunnel, thence into\n\
room %d!\n",
cave[arrow_location].tunnel[link]);
else
(void)printf(
"*thunk* The arrow can't find a way from %d to %d and flys randomly\n\
into room %d!\n",
arrow_location, next,
cave[arrow_location].tunnel[link]);
arrow_location = cave[arrow_location].tunnel[link];
break;
}
chance = random() % 10;
if (roomcnt == 3 && chance < 2) {
(void)printf(
"Your bowstring breaks! *twaaaaaang*\n\
The arrow is weakly shot and can go no further!\n");
break;
} else if (roomcnt == 4 && chance < 6) {
(void)printf(
"The arrow wavers in its flight and and can go no further!\n");
break;
}
}
/*
* now we've gotten into the new room let us see if El Wumpo is
* in the same room ... if so we've a HIT and the player WON!
*/
if (arrow_location == wumpus_loc) {
kill_wump();
return(1);
}
if (arrow_location == player_loc) {
shoot_self();
return(1);
}
if (!--arrows_left) {
no_arrows();
return(1);
}
{
/* each time you shoot, it's more likely the wumpus moves */
static int lastchance = 2;
if (((random() % level == EASY ? 12 : 9) < (lastchance += 2))) {
move_wump();
if (wumpus_loc == player_loc)
wump_kill();
lastchance = random() % 3;
}
}
return(0);
}
int
gcd(a, b)
int a, b;
{
int r;
r = a % b;
if (r == 0)
return (b);
return (gcd(b, r));
}
void
cave_init()
{
int i, j, k, link;
int delta;
/*
* This does most of the interesting work in this program actually!
* In this routine we'll initialize the Wumpus cave to have all rooms
* linking to all others by stepping through our data structure once,
* recording all forward links and backwards links too. The parallel
* "linkcount" data structure ensures that no room ends up with more
* than three links, regardless of the quality of the random number
* generator that we're using.
*/
srandom((int)time((time_t *)0));
/* initialize the cave first off. */
for (i = 1; i <= room_num; ++i)
for (j = 0; j < link_num ; ++j)
cave[i].tunnel[j] = -1;
/*
* Choose a random 'hop' delta for our guaranteed link.
* To keep the cave connected, we need the greatest common divisor
* of (delta + 1) and room_num to be 1.
*/
do {
delta = (random() % (room_num - 1)) + 1;
} while (gcd(room_num, delta + 1) != 1);
for (i = 1; i <= room_num; ++i) {
link = ((i + delta) % room_num) + 1; /* connection */
cave[i].tunnel[0] = link; /* forw link */
cave[link].tunnel[1] = i; /* back link */
}
/* now fill in the rest of the cave with random connections */
for (i = 1; i <= room_num; i++)
for (j = 2; j < link_num ; j++) {
if (cave[i].tunnel[j] != -1)
continue;
try_again: link = (random() % room_num) + 1;
/* skip duplicates */
for (k = 0; k < j; k++)
if (cave[i].tunnel[k] == link)
goto try_again;
cave[i].tunnel[j] = link;
if (random() % 2 == 1)
continue;
for (k = 0; k < link_num; ++k) {
/* if duplicate, skip it */
if (cave[link].tunnel[k] == i)
k = link_num;
/* if open link, use it, force exit */
if (cave[link].tunnel[k] == -1) {
cave[link].tunnel[k] = i;
k = link_num;
}
}
}
/*
* now that we're done, sort the tunnels in each of the rooms to
* make it easier on the intrepid adventurer.
*/
for (i = 1; i <= room_num; ++i)
qsort(cave[i].tunnel, (u_int)link_num,
sizeof(cave[i].tunnel[0]), int_compare);
#ifdef DEBUG
if (debug)
for (i = 1; i <= room_num; ++i) {
(void)printf("<room %d has tunnels to ", i);
for (j = 0; j < link_num; ++j)
(void)printf("%d ", cave[i].tunnel[j]);
(void)printf(">\n");
}
#endif
}
void
clear_things_in_cave()
{
int i;
/*
* remove bats and pits from the current cave in preparation for us
* adding new ones via the initialize_things_in_cave() routines.
*/
for (i = 1; i <= room_num; ++i)
cave[i].has_a_bat = cave[i].has_a_pit = 0;
}
void
initialize_things_in_cave()
{
int i, loc;
/* place some bats, pits, the wumpus, and the player. */
for (i = 0; i < bat_num; ++i) {
do {
loc = (random() % room_num) + 1;
} while (cave[loc].has_a_bat);
cave[loc].has_a_bat = 1;
#ifdef DEBUG
if (debug)
(void)printf("<bat in room %d>\n", loc);
#endif
}
for (i = 0; i < pit_num; ++i) {
do {
loc = (random() % room_num) + 1;
} while (cave[loc].has_a_pit && cave[loc].has_a_bat);
cave[loc].has_a_pit = 1;
#ifdef DEBUG
if (debug)
(void)printf("<pit in room %d>\n", loc);
#endif
}
wumpus_loc = (random() % room_num) + 1;
#ifdef DEBUG
if (debug)
(void)printf("<wumpus in room %d>\n", loc);
#endif
do {
player_loc = (random() % room_num) + 1;
} while (player_loc == wumpus_loc || (level == HARD ?
(link_num / room_num < 0.4 ? wump_nearby() : 0) : 0));
}
int
getans(prompt)
const char *prompt;
{
char buf[20];
/*
* simple routine to ask the yes/no question specified until the user
* answers yes or no, then return 1 if they said 'yes' and 0 if they
* answered 'no'.
*/
for (;;) {
(void)printf("%s", prompt);
(void)fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin))
return(0);
if (*buf == 'N' || *buf == 'n')
return(0);
if (*buf == 'Y' || *buf == 'y')
return(1);
(void)printf(
"I don't understand your answer; please enter 'y' or 'n'!\n");
}
/* NOTREACHED */
}
int
bats_nearby()
{
int i;
/* check for bats in the immediate vicinity */
for (i = 0; i < link_num; ++i)
if (cave[cave[player_loc].tunnel[i]].has_a_bat)
return(1);
return(0);
}
int
pit_nearby()
{
int i;
/* check for pits in the immediate vicinity */
for (i = 0; i < link_num; ++i)
if (cave[cave[player_loc].tunnel[i]].has_a_pit)
return(1);
return(0);
}
int
wump_nearby()
{
int i, j;
/* check for a wumpus within TWO caves of where we are */
for (i = 0; i < link_num; ++i) {
if (cave[player_loc].tunnel[i] == wumpus_loc)
return(1);
for (j = 0; j < link_num; ++j)
if (cave[cave[player_loc].tunnel[i]].tunnel[j] ==
wumpus_loc)
return(1);
}
return(0);
}
void
move_wump()
{
wumpus_loc = cave[wumpus_loc].tunnel[random() % link_num];
}
int
int_compare(a, b)
const void *a, *b;
{
return(*(const int *)a < *(const int *)b ? -1 : 1);
}
char* instructionsStr = "Welcome to the game of Hunt the Wumpus.\n\nThe Wumpus typically lives in a cave of twenty rooms, with each room having\nthree tunnels connecting it to other rooms in the cavern. Caves may vary,\nhowever, depending on options specified when starting the game.\n\nThe game has the following hazards for intrepid adventurers to wind their\nway through:\n\n Pits -- If you fall into one of the bottomless pits, you find yourself\n\tslung back out on the far side of the Earth and in very poor\n\tshape to continue your quest since you're dead.\n\n Bats -- As with any other cave, the Wumpus cave has bats in residence.\n\tThese are a bit more potent, however, and if you stumble into\n\tone of their rooms they will rush up and carry you elsewhere in\n\tthe cave.\n \n Wumpus -- If you happen to walk into the room the Wumpus is in you'll find\n\tthat he has quite an appetite for young adventurous humans! Not\n\trecommended.\n\nThe Wumpus, by the way, is not bothered by the hazards since he has sucker\nfeet and is too big for a bat to lift. If you try to shoot him and miss,\nthere's also a chance that he'll up and move himself into another cave,\nthough by nature the Wumpus is a sedentary creature.\n\nEach turn you may either move or shoot a crooked arrow. Moving is done\nsimply by specifying \"m\" for move and the number of the room that you'd\nlike to move down a tunnel towards. Shooting is done similarly; indicate\nthat you'd like to shoot one of your magic arrows with an \"s\" for shoot,\nthen list a set of connected room numbers through which the deadly shaft\nshould fly!\n\nIf your path for the arrow is incorrect, however, it will flail about in\nthe room it can't understand and randomly pick a tunnel to continue\nthrough. You might just end up shooting yourself in the foot if you're\nnot careful! On the other hand, if you shoot the Wumpus you've WON!\n\nGood luck.";
void
instructions()
{
if (!getans("Instructions? (y-n) "))
return;
(void)printf(instructionsStr);
}
void
usage()
{
(void)fprintf(stderr,
"usage: wump [-h] [-a arrows] [-b bats] [-p pits] [-r rooms] [-t tunnels]\n");
exit(1);
}
/* messages */
void
wump_kill()
{
(void)printf(
"*ROAR* *chomp* *snurfle* *chomp*!\n\
Much to the delight of the Wumpus, you walked right into his mouth,\n\
making you one of the easiest dinners he's ever had! For you, however,\n\
it's a rather unpleasant death. The only good thing is that it's been\n\
so long since the evil Wumpus cleaned his teeth that you immediately\n\
passed out from the stench!\n");
}
void
kill_wump()
{
(void)printf(
"*thwock!* *groan* *crash*\n\n\
A horrible roar fills the cave, and you realize, with a smile, that you\n\
have slain the evil Wumpus and won the game! You don't want to tarry for\n\
long, however, because not only is the Wumpus famous, but the stench of\n\
dead Wumpus is also quite well known, a stench plenty enough to slay the\n\
mightiest adventurer at a single whiff!!\n");
}
void
no_arrows()
{
(void)printf(
"\nYou turn and look at your quiver, and realize with a sinking feeling\n\
that you've just shot your last arrow (figuratively, too). Sensing this\n\
with its psychic powers, the evil Wumpus rampagees through the cave, finds\n\
you, and with a mighty *ROAR* eats you alive!\n");
}
void
shoot_self()
{
(void)printf(
"\n*Thwack!* A sudden piercing feeling informs you that the ricochet\n\
of your wild arrow has resulted in it wedging in your side, causing\n\
extreme agony. The evil Wumpus, with its psychic powers, realizes this\n\
and immediately rushes to your side, not to help, alas, but to EAT YOU!\n\
(*CHOMP*)\n");
}
void
jump(where)
int where;
{
(void)printf(
"\nWith a jaunty step you enter the magic tunnel. As you do, you\n\
notice that the walls are shimmering and glowing. Suddenly you feel\n\
a very curious, warm sensation and find yourself in room %d!!\n", where);
}
void
pit_kill()
{
(void)printf(
"*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
The whistling sound and updraft as you walked into this room of the\n\
cave apparently wasn't enough to clue you in to the presence of the\n\
bottomless pit. You have a lot of time to reflect on this error as\n\
you fall many miles to the core of the earth. Look on the bright side;\n\
you can at least find out if Jules Verne was right...\n");
}
void
pit_survive()
{
(void)printf(
"Without conscious thought you grab for the side of the cave and manage\n\
to grasp onto a rocky outcrop. Beneath your feet stretches the limitless\n\
depths of a bottomless pit! Rock crumbles beneath your feet!\n");
}

View file

@ -237,7 +237,9 @@ int sys_munlock(const void *, size_t) libcesque;
int sys_munlockall(void) libcesque;
int sys_personality(uint64_t) libcesque;
int sys_ptrace(int, ...) libcesque;
int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
int sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque;
int sysctlbyname(const char *, void *, size_t *, void *, size_t) libcesque;
int sysctlnametomib(const char *, int *, size_t *) libcesque;
int tmpfd(void) libcesque;
int touch(const char *, unsigned) libcesque;
int unveil(const char *, const char *) libcesque;

View file

@ -53,7 +53,7 @@ static dontinline int __clk_tck_init(void) {
cmd[0] = 1; // CTL_KERN
cmd[1] = 12; // KERN_CLOCKRATE
len = sizeof(clock);
if (sys_sysctl(cmd, 2, &clock, &len, NULL, 0) != -1) {
if (sysctl(cmd, 2, &clock, &len, NULL, 0) != -1) {
x = clock.hz;
} else {
x = -1;

View file

@ -67,7 +67,7 @@ int sys_clock_gettime_xnu(int clock, struct timespec *ts) {
struct timeval x;
size_t n = sizeof(x);
int mib[] = {CTL_KERN, KERN_BOOTTIME};
if (sys_sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1)
if (sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1)
return -1;
if (ts)
*ts = timeval_totimespec(timeval_sub(timeval_real(), x));

View file

@ -332,7 +332,8 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
if (__isfdkind(fd, kFdFile) || //
__isfdkind(fd, kFdSocket) || //
__isfdkind(fd, kFdConsole) || //
__isfdkind(fd, kFdDevNull)) {
__isfdkind(fd, kFdDevNull) || //
__isfdkind(fd, kFdDevRandom)) {
if (cmd == F_GETFL) {
rc = g_fds.p[fd].flags & (O_ACCMODE | _O_APPEND | _O_DIRECT |
_O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL);

View file

@ -104,6 +104,7 @@ textwindows int sys_fstat_nt(int fd, struct stat *st) {
return ebadf();
case kFdConsole:
case kFdDevNull:
case kFdDevRandom:
return sys_fstat_nt_special(g_fds.p[fd].kind, st);
case kFdSocket:
return sys_fstat_nt_socket(g_fds.p[fd].kind, st);

View file

@ -59,6 +59,8 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st,
return sys_fstat_nt_special(kFdConsole, st);
} else if (!strcmp(path + 5, "null")) {
return sys_fstat_nt_special(kFdDevNull, st);
} else if (!strcmp(path + 5, "random") || !strcmp(path + 5, "urandom")) {
return sys_fstat_nt_special(kFdDevRandom, st);
} else if (!strcmp(path + 5, "stdin")) {
return sys_fstat_nt(STDIN_FILENO, st);
} else if (!strcmp(path + 5, "stdout")) {

View file

@ -51,7 +51,7 @@ static int __get_cpu_count_bsd(void) {
} else {
cmd[1] = HW_NCPU;
}
if (!sys_sysctl(cmd, 2, &c, &n, 0, 0)) {
if (!sysctl(cmd, 2, &c, &n, 0, 0)) {
return c;
} else {
return -1;

View file

@ -24,7 +24,7 @@
int gethostname_bsd(char *name, size_t len, int kind) {
int cmd[2] = {CTL_KERN, kind};
if (sys_sysctl(cmd, 2, name, &len, 0, 0) != -1) {
if (sysctl(cmd, 2, name, &len, 0, 0) != -1) {
return 0;
} else {
if (errno == ENOMEM) {

View file

@ -64,7 +64,7 @@ int getloadavg(double *a, int n) {
struct loadavg loadinfo;
int mib[2] = {CTL_VM, VM_LOADAVG};
size = sizeof(loadinfo);
if ((rc = sys_sysctl(mib, 2, &loadinfo, &size, 0, 0)) != -1) {
if ((rc = sysctl(mib, 2, &loadinfo, &size, 0, 0)) != -1) {
for (i = 0; i < n; i++) {
a[i] = (double)loadinfo.ldavg[i] / loadinfo.fscale;
}

View file

@ -195,7 +195,7 @@ static inline void InitProgramExecutableNameImpl(void) {
cmd[2] = KERN_PROC_PATHNAME_NETBSD;
}
cmd[3] = -1; // current process
if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) {
if (sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) {
if (!OldApeLoader(b)) {
goto UseBuf;
}

View file

@ -70,7 +70,7 @@ static void GetRandomArnd(char *p, size_t n) {
cmd[0] = 1; // CTL_KERN
cmd[1] = IsFreebsd() ? 37 : 81; // KERN_ARND
unassert((m = n) <= 256);
if (sys_sysctl(cmd, 2, p, &n, 0, 0) == -1)
if (sysctl(cmd, 2, p, &n, 0, 0) == -1)
notpossible;
if (m != n)
notpossible;

View file

@ -24,7 +24,6 @@
#include "libc/calls/termios.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/serialize.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
@ -42,6 +41,7 @@
#include "libc/nt/winsock.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/serialize.h"
#include "libc/sock/internal.h"
#include "libc/sock/struct/ifconf.h"
#include "libc/sock/struct/ifreq.h"
@ -66,7 +66,7 @@ static struct HostAdapterInfoNode {
struct sockaddr netmask;
struct sockaddr broadcast;
short flags;
} *__hostInfo;
} * __hostInfo;
static int ioctl_default(int fd, unsigned long request, void *arg) {
int rc;
@ -107,8 +107,9 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
*arg = MAX(0, bytes);
return 0;
} else if (g_fds.p[fd].kind == kFdDevNull) {
*arg = 1;
return 0;
return enotty();
} else if (g_fds.p[fd].kind == kFdDevRandom) {
return einval();
} else if (GetFileType(handle) == kNtFileTypePipe) {
uint32_t avail;
if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) {

View file

@ -55,6 +55,7 @@ bool32 ischardev(int fd) {
}
} else {
return __isfdkind(fd, kFdConsole) || __isfdkind(fd, kFdDevNull) ||
__isfdkind(fd, kFdDevRandom) ||
(__isfdkind(fd, kFdFile) &&
GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar);
}

View file

@ -62,7 +62,7 @@ static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) {
}
textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) {
if (__isfdkind(fd, kFdDevNull)) {
if (__isfdkind(fd, kFdDevNull) || __isfdkind(fd, kFdDevRandom)) {
return offset;
} else if (__isfdkind(fd, kFdFile)) {
struct Fd *f = g_fds.p + fd;

View file

@ -159,6 +159,15 @@ static textwindows int sys_open_nt_special(int fd, int flags, int mode,
return fd;
}
static textwindows int sys_open_nt_no_handle(int fd, int flags, int mode,
int kind) {
g_fds.p[fd].kind = kind;
g_fds.p[fd].mode = mode;
g_fds.p[fd].flags = flags;
g_fds.p[fd].handle = -1;
return fd;
}
static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) {
int64_t handle;
if (!__isfdopen(oldfd)) {
@ -211,6 +220,8 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags,
rc = sys_open_nt_special(fd, flags, mode, kFdConsole, u"CONIN$");
} else if (!strcmp(file + 5, "null")) {
rc = sys_open_nt_special(fd, flags, mode, kFdDevNull, u"NUL");
} else if (!strcmp(file + 5, "urandom") || !strcmp(file + 5, "random")) {
rc = sys_open_nt_no_handle(fd, flags, mode, kFdDevRandom);
} else if (!strcmp(file + 5, "stdin")) {
rc = sys_open_nt_dup(fd, flags, mode, STDIN_FILENO);
} else if (!strcmp(file + 5, "stdout")) {

View file

@ -72,7 +72,8 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
rc = sys_pread(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdSocket)) {
rc = espipe();
} else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) {
} else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull) ||
__isfdkind(fd, kFdDevRandom)) {
rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, offset);
} else {
rc = ebadf();

View file

@ -40,6 +40,8 @@ static const char *__fdkind2str(int x) {
return "kFdZip";
case kFdEpoll:
return "kFdEpoll";
case kFdDevRandom:
return "kFdRandom";
default:
return "kFdWut";
}

View file

@ -65,7 +65,8 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
rc = sys_pwrite(fd, buf, size, offset, offset);
} else if (__isfdkind(fd, kFdSocket)) {
rc = espipe();
} else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) {
} else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull) ||
__isfdkind(fd, kFdDevRandom)) {
rc = sys_write_nt(fd, (struct iovec[]){{(void *)buf, size}}, 1, offset);
} else {
return ebadf();

View file

@ -786,6 +786,10 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset,
return 0;
}
if (f->kind == kFdDevRandom) {
return ProcessPrng(data, size) ? size : __winerr();
}
if (f->kind == kFdConsole) {
return ReadFromConsole(f, data, size, waitmask);
}

View file

@ -29,6 +29,7 @@ textwindows ssize_t sys_readv_nt(int fd, const struct iovec *iov, int iovlen) {
case kFdFile:
case kFdConsole:
case kFdDevNull:
case kFdDevRandom:
return sys_read_nt(fd, iov, iovlen, -1);
case kFdSocket:
return _weaken(sys_recv_nt)(fd, iov, iovlen, 0);

View file

@ -61,7 +61,7 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset,
bool pwriting = offset != -1;
bool seekable =
(f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk) ||
f->kind == kFdDevNull;
f->kind == kFdDevNull || f->kind == kFdDevRandom;
if (pwriting && !seekable) {
return espipe();
}

View file

@ -2,15 +2,16 @@
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_
COSMOPOLITAN_C_START_
#define kFdEmpty 0
#define kFdFile 1
#define kFdSocket 2
#define kFdConsole 4
#define kFdSerial 5
#define kFdZip 6
#define kFdEpoll 7
#define kFdReserved 8
#define kFdDevNull 9
#define kFdEmpty 0
#define kFdFile 1
#define kFdSocket 2
#define kFdConsole 4
#define kFdSerial 5
#define kFdZip 6
#define kFdEpoll 7
#define kFdReserved 8
#define kFdDevNull 9
#define kFdDevRandom 10
struct Fd {
char kind;

33
libc/calls/sysctl.c Normal file
View file

@ -0,0 +1,33 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/runtime/syslib.internal.h"
int sys_sysctl(int *, unsigned, void *, size_t *, void *, size_t) libcesque;
int sysctl(int *name, unsigned namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen) {
if (__syslib && __syslib->__version >= 10) {
return _sysret(
__syslib->__sysctl(name, namelen, oldp, oldlenp, newp, newlen));
} else {
return sys_sysctl(name, namelen, oldp, oldlenp, newp, newlen);
}
}

31
libc/calls/sysctlbyname.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/errfuns.h"
int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp,
size_t newlen) {
if (__syslib && __syslib->__version >= 10) {
return _sysret(__syslib->__sysctlbyname(name, oldp, oldlenp, newp, newlen));
} else {
return enosys();
}
}

View file

@ -0,0 +1,30 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/errfuns.h"
int sysctlnametomib(const char *name, int *mibp, size_t *sizep) {
if (__syslib && __syslib->__version >= 10) {
return _sysret(__syslib->__sysctlnametomib(name, mibp, sizep));
} else {
return enosys();
}
}

View file

@ -46,7 +46,7 @@ static int64_t GetUptime(void) {
struct timeval x;
size_t n = sizeof(x);
int mib[] = {CTL_KERN, KERN_BOOTTIME};
if (sys_sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1)
if (sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1)
return 0;
return timespec_real().tv_sec - x.tv_sec;
}
@ -55,7 +55,7 @@ static int64_t GetPhysmem(void) {
uint64_t x = 0;
size_t n = sizeof(x);
int mib[] = {CTL_HW, HW_PHYSMEM};
if (sys_sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1)
if (sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1)
return 0;
return x;
}
@ -65,7 +65,7 @@ static void GetLoads(uint64_t loads[3]) {
struct loadavg loadinfo;
int mib[2] = {CTL_VM, VM_LOADAVG};
size = sizeof(loadinfo);
if (sys_sysctl(mib, 2, &loadinfo, &size, 0, 0) != -1) {
if (sysctl(mib, 2, &loadinfo, &size, 0, 0) != -1) {
for (int i = 0; i < 3; i++) {
loads[i] = (double)loadinfo.ldavg[i] / loadinfo.fscale * 65536;
}

View file

@ -62,7 +62,7 @@ static void GetBsdStr(int c0, int c1, char *s) {
size_t n = SYS_NMLN;
int cmd[2] = {c0, c1};
bzero(s, n), --n;
sys_sysctl(cmd, 2, s, &n, NULL, 0);
sysctl(cmd, 2, s, &n, NULL, 0);
errno = e;
// sysctl kern.version is too verbose for uname
if ((p = strchr(s, '\n'))) {

View file

@ -52,6 +52,11 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
struct Fd *f = g_fds.p + fd;
bool isconsole = f->kind == kFdConsole;
// not implemented, XNU returns eperm();
if (f->kind == kFdDevRandom) {
return eperm();
}
// determine win32 handle for writing
int64_t handle = f->handle;
if (isconsole && _weaken(GetConsoleOutputHandle)) {

View file

@ -28,6 +28,7 @@ textwindows ssize_t sys_writev_nt(int fd, const struct iovec *iov, int iovlen) {
case kFdFile:
case kFdConsole:
case kFdDevNull:
case kFdDevRandom:
return sys_write_nt(fd, iov, iovlen, -1);
case kFdSocket:
return _weaken(sys_send_nt)(fd, iov, iovlen, 0);

View file

@ -4,7 +4,7 @@
#define __COSMOPOLITAN_MAJOR__ 3
#define __COSMOPOLITAN_MINOR__ 3
#define __COSMOPOLITAN_PATCH__ 3
#define __COSMOPOLITAN_PATCH__ 4
#define __COSMOPOLITAN__ \
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
__COSMOPOLITAN_PATCH__)

View file

@ -254,10 +254,28 @@ static relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
kprintf("\n");
}
static inline void SpinLock(atomic_uint *lock) {
int x;
for (;;) {
x = atomic_exchange_explicit(lock, 1, memory_order_acquire);
if (!x)
break;
}
}
static inline void SpinUnlock(atomic_uint *lock) {
atomic_store_explicit(lock, 0, memory_order_release);
}
relegated void __oncrash(int sig, struct siginfo *si, void *arg) {
static atomic_uint lock;
BLOCK_CANCELATION;
SpinLock(&lock);
int err = errno;
__restore_tty();
ShowCrashReport(err, sig, si, arg);
SpinUnlock(&lock);
ALLOW_CANCELATION;
}
#endif /* __x86_64__ */

View file

@ -18,6 +18,7 @@
*/
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/aarch64.internal.h"
@ -32,6 +33,7 @@
#include "libc/calls/ucontext.h"
#include "libc/cxxabi.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h"
@ -377,10 +379,25 @@ static relegated void __oncrash_impl(int sig, struct siginfo *si,
klog(b->p, MIN(b->i, b->n));
}
static inline void SpinLock(atomic_uint *lock) {
int x;
for (;;) {
x = atomic_exchange_explicit(lock, 1, memory_order_acquire);
if (!x)
break;
}
}
static inline void SpinUnlock(atomic_uint *lock) {
atomic_store_explicit(lock, 0, memory_order_release);
}
relegated void __oncrash(int sig, struct siginfo *si, void *arg) {
ucontext_t *ctx = arg;
static atomic_uint lock;
BLOCK_CANCELATION;
__oncrash_impl(sig, si, ctx);
SpinLock(&lock);
__oncrash_impl(sig, si, arg);
SpinUnlock(&lock);
ALLOW_CANCELATION;
}

View file

@ -74,9 +74,9 @@ char *GetInterpreterExecutableName(char *p, size_t n) {
cmd[2] = 12; // KERN_PROC_PATHNAME
} else { //
cmd[2] = 5; // KERN_PROC_PATHNAME
} //
cmd[3] = -1; // current process
if (sys_sysctl(cmd, ARRAYLEN(cmd), p, &n, 0, 0) != -1) {
} //
cmd[3] = -1; // current process
if (sysctl(cmd, ARRAYLEN(cmd), p, &n, 0, 0) != -1) {
errno = e;
return p;
}

View file

@ -23,7 +23,7 @@ long __get_sysctl(int x, int y) {
int value;
int mib[2] = {x, y};
size_t len = sizeof(value);
if (sys_sysctl(mib, 2, &value, &len, 0, 0) != -1) {
if (sysctl(mib, 2, &value, &len, 0, 0) != -1) {
return value;
} else {
return -1;

View file

@ -12,7 +12,7 @@ COSMOPOLITAN_C_START_
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
#define SYSLIB_VERSION 9 /* sync with ape/ape-m1.c */
#define SYSLIB_VERSION 10 /* sync with ape/ape-m1.c */
/* if this number increases, then everyone on macos arm will need to
reinstall ape loader in order to run newer ape binaries so please
@ -82,6 +82,9 @@ struct Syslib {
char *(*__dlerror)(void);
/* v9 (2024-01-31) */
int (*__pthread_cpu_number_np)(size_t *);
long (*__sysctl)(int *, unsigned, void *, size_t *, void *, size_t);
long (*__sysctlbyname)(const char *, void *, size_t *, void *, size_t);
long (*__sysctlnametomib)(const char *, int *, size_t *);
};
extern struct Syslib *__syslib;

View file

@ -70,6 +70,9 @@ ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
if (__isfdkind(fd, kFdSocket)) {
rc = sys_recvfrom_nt(fd, (struct iovec[]){{buf, size}}, 1, flags, &addr,
&addrsize);
if (rc != -1 && addrsize == sizeof(addr)) {
addrsize = 0;
}
} else if (__isfdkind(fd, kFdFile) && !opt_out_srcaddr) { /* socketpair */
if (!flags) {
rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, -1);
@ -84,10 +87,14 @@ ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
}
if (rc != -1) {
if (IsBsd()) {
__convert_bsd_to_sockaddr(&addr);
if (addrsize) {
if (IsBsd()) {
__convert_bsd_to_sockaddr(&addr);
}
__write_sockaddr(&addr, opt_out_srcaddr, opt_inout_srcaddrsize);
} else {
*opt_inout_srcaddrsize = 0;
}
__write_sockaddr(&addr, opt_out_srcaddr, opt_inout_srcaddrsize);
}
END_CANCELATION_POINT;

View file

@ -57,11 +57,7 @@ char *fgets_unlocked(char *s, int size, FILE *f) {
break;
} else {
if ((c = fgetc_unlocked(f)) == -1) {
if (ferror_unlocked(f) == EINTR) {
continue;
} else {
break;
}
break;
}
*p++ = c & 255;
if (c == '\n')

View file

@ -20,15 +20,19 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/files.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
int pipefd[2];
int stdoutBack;
int allowMask;
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown();
// qemu-aarch64 defines o_largefile wrong
allowMask = ~(O_LARGEFILE | 0x00008000);
}
void CaptureStdout(void) {
@ -46,8 +50,7 @@ void RestoreStdout(void) {
TEST(specialfile, devNull) {
ASSERT_SYS(0, 3, creat("/dev/null", 0644));
// qemu-aarch64 defines o_largefile wrong
ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & ~(O_LARGEFILE | 0x00008000));
ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & allowMask);
ASSERT_SYS(0, 2, write(3, "hi", 2));
ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 0));
ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 2));
@ -64,12 +67,51 @@ TEST(specialfile, devNull) {
TEST(specialfile, devNullRead) {
char buf[8] = {0};
ASSERT_SYS(0, 3, open("/dev/null", O_RDONLY));
// qemu-aarch64 defines o_largefile wrong
ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & ~(O_LARGEFILE | 0x00008000));
ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & allowMask);
ASSERT_SYS(0, 0, read(3, buf, 8));
ASSERT_SYS(0, 0, close(3));
}
TEST(specialfile, devRandomRead) {
char buf[8] = {0};
ASSERT_SYS(0, 3, open("/dev/random", O_RDONLY));
ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & allowMask);
ASSERT_SYS(0, 8, read(3, buf, 8));
ASSERT_NE(0, memcmp(buf, " ", 8));
ASSERT_SYS(0, 0, close(3));
}
TEST(specialfile, devUrandomRead) {
char buf[8] = {0};
ASSERT_SYS(0, 3, open("/dev/urandom", O_RDONLY));
ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & allowMask);
ASSERT_SYS(0, 8, read(3, buf, 8));
ASSERT_NE(0, memcmp(buf, " ", 8));
ASSERT_SYS(0, 0, close(3));
}
TEST(specialfile, devRandomWrite_fails_on_nt) {
if (!IsWindows()) {
return;
}
char buf[8] = {0};
ASSERT_SYS(0, 3, creat("/dev/random", 0644));
ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & allowMask);
ASSERT_SYS(EPERM, -1, write(3, buf, 8));
ASSERT_SYS(0, 0, close(3));
}
TEST(specialfile, devUrandomWrite_fails_on_nt) {
if (!IsWindows()) {
return;
}
char buf[8] = {0};
ASSERT_SYS(0, 3, creat("/dev/urandom", 0644));
ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & allowMask);
ASSERT_SYS(EPERM, -1, write(3, buf, 8));
ASSERT_SYS(0, 0, close(3));
}
TEST(specialfile, devStdout) {
char buf[8] = {8};
CaptureStdout();

View file

@ -33,9 +33,9 @@
__static_yoink("zipos");
__static_yoink("libc/testlib/hyperion.txt");
__static_yoink("_Cz_inflate");
__static_yoink("_Cz_inflateInit2");
__static_yoink("_Cz_inflateEnd");
__static_yoink("inflate");
__static_yoink("inflateInit2");
__static_yoink("inflateEnd");
void *Worker(void *arg) {
int i, fd;

View file

@ -25,6 +25,7 @@
#include "libc/runtime/syslib.internal.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/sockaddr.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/ipproto.h"
@ -107,7 +108,7 @@ TEST(O_NONBLOCK, canBeTunedWithFcntl_toMakeReadNonBlocking) {
PARENT();
EXPECT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, O_RDWR, fcntl(3, F_GETFL));
ASSERT_SYS(0, O_RDWR, fcntl(3, F_GETFL) & O_ACCMODE); // QEMU O_LARGEFILE :(
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr)));
ASSERT_SYS(0, 0, fcntl(3, F_SETFL, O_RDWR | O_NONBLOCK));
ASSERT_SYS(EAGAIN, -1, read(3, buf, 16));

View file

@ -93,3 +93,44 @@ TEST(recvfrom, test) {
EXPECT_SYS(0, 0, close(client1));
WAIT(exit, 0);
}
// server listens for connections, accepts a connection, and sends data
// client connects to server recieves with recvfrom and verifies addrsize
// is 0 as the sender info isn't available on connection sockets.
TEST(recvfrom, tcp) {
uint32_t addrsize = sizeof(struct sockaddr_in);
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(0x7f000001),
};
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&server, sizeof(server)));
ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&server, &addrsize));
ASSERT_SYS(0, 0, listen(3, 5));
////////////////////////////////////////////////////////////////////////////////
SPAWN(fork);
struct sockaddr_in data, addr;
uint32_t addrsize = sizeof(struct sockaddr_in);
EXPECT_SYS(0, 0, close(3));
ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&server, sizeof(server)));
ASSERT_SYS(
0, sizeof(data),
recvfrom(3, &data, sizeof(data), 0, (struct sockaddr *)&addr, &addrsize));
ASSERT_EQ(0, addrsize);
EXPECT_SYS(0, 0, close(3));
////////////////////////////////////////////////////////////////////////////////
PARENT();
int client;
struct sockaddr client_sockaddr;
uint32_t sockaddr_size = sizeof(client_sockaddr);
ASSERT_NE(-1, (client = accept(3, &client_sockaddr, &sockaddr_size)));
ASSERT_SYS(0, sizeof(client_sockaddr),
sendto(client, &client_sockaddr, sizeof(client_sockaddr), 0,
(struct sockaddr *)&server, sizeof(server)));
EXPECT_SYS(0, 0, close(client));
WAIT(exit, 0);
EXPECT_SYS(0, 0, close(3));
}

View file

@ -1,476 +0,0 @@
/*-*-mode:c++;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8-*-│
vi: set et ft=cpp ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include "libc/stdio/rand.h"
#define PRECISION 2e-6
#define LV1DCACHE 49152
#define THRESHOLD 3000000
#if defined(__OPTIMIZE__) && !defined(__SANITIZE_ADDRESS__)
#define ITERATIONS 5
#else
#define ITERATIONS 1
#endif
#define OPTIMIZED __attribute__((__optimize__("-O3,-ffast-math")))
#define PORTABLE \
__target_clones("arch=znver4," \
"arch=znver3," \
"arch=sapphirerapids," \
"arch=alderlake," \
"arch=rocketlake," \
"arch=cooperlake," \
"arch=tigerlake," \
"arch=cascadelake," \
"arch=skylake-avx512," \
"arch=skylake," \
"arch=znver1," \
"arch=tremont," \
"fma," \
"avx")
static bool is_self_testing;
// m×n → n×m
template <typename TA, typename TB>
void transpose(long m, long n, const TA *A, long lda, TB *B, long ldb) {
#pragma omp parallel for collapse(2) if (m * n > THRESHOLD)
for (long i = 0; i < m; ++i)
for (long j = 0; j < n; ++j) {
B[ldb * j + i] = A[lda * i + j];
}
}
// m×k * k×n → m×n
// k×m * k×n → m×n if aᵀ
// m×k * n×k → m×n if bᵀ
// k×m * n×k → m×n if aᵀ and bᵀ
template <typename TC, typename TA, typename TB>
void dgemm(bool aᵀ, bool bᵀ, long m, long n, long k, float α, const TA *A,
long lda, const TB *B, long ldb, float β, TC *C, long ldc) {
#pragma omp parallel for collapse(2) if (m * n * k > THRESHOLD)
for (long i = 0; i < m; ++i)
for (long j = 0; j < n; ++j) {
double sum = 0;
for (long l = 0; l < k; ++l)
sum = std::fma((aᵀ ? A[lda * l + i] : A[lda * i + l]) * α,
(bᵀ ? B[ldb * j + l] : B[ldb * l + j]), sum);
C[ldc * i + j] = C[ldc * i + j] * β + sum;
}
}
template <typename T, typename TC, typename TA, typename TB>
struct Gemmlin {
public:
Gemmlin(bool aT, bool bT, float α, const TA *A, long lda, const TB *B,
long ldb, float β, TC *C, long ldc)
: aT(aT),
bT(bT),
α(α),
A(A),
lda(lda),
B(B),
ldb(ldb),
β(β),
C(C),
ldc(ldc) {
}
void gemm(long m, long n, long k) {
if (!m || !n)
return;
for (long i = 0; i < m; ++i)
for (long j = 0; j < n; ++j) {
C[ldc * i + j] *= β;
}
if (!k)
return;
cub = sqrt(LV1DCACHE) / sqrt(sizeof(T) * 3);
mnpack(0, m, 0, n, 0, k);
}
private:
void mnpack(long m0, long m, //
long n0, long n, //
long k0, long k) {
long mc = rounddown(std::min(m - m0, cub), 4);
long mp = m0 + (m - m0) / mc * mc;
long nc = rounddown(std::min(n - n0, cub), 4);
long np = n0 + (n - n0) / nc * nc;
long kc = rounddown(std::min(k - k0, cub), 4);
long kp = k0 + (k - k0) / kc * kc;
kpack(m0, mc, mp, n0, nc, np, k0, kc, k, kp);
if (m - mp)
mnpack(mp, m, n0, np, k0, k);
if (n - np)
mnpack(m0, mp, np, n, k0, k);
if (m - mp && n - np)
mnpack(mp, m, np, n, k0, k);
}
void kpack(long m0, long mc, long m, //
long n0, long nc, long n, //
long k0, long kc, long k, //
long kp) {
rpack(m0, mc, m, n0, nc, n, k0, kc, kp);
if (k - kp)
rpack(m0, mc, m, n0, nc, n, kp, k - kp, k);
}
void rpack(long m0, long mc, long m, //
long n0, long nc, long n, //
long k0, long kc, long k) {
if (!(mc % 4) && !(nc % 4))
bgemm<4, 4>(m0, mc, m, n0, nc, n, k0, kc, k);
else
bgemm<1, 1>(m0, mc, m, n0, nc, n, k0, kc, k);
}
template <int mr, int nr>
void bgemm(long m0, long mc, long m, //
long n0, long nc, long n, //
long k0, long kc, long k) {
ops = (m - m0) * (n - n0) * (k - k0);
ml = (m - m0) / mc;
nl = (n - n0) / nc;
locks = new lock[ml * nl];
there_will_be_blocks<mr, nr>(m0, mc, m, n0, nc, n, k0, kc, k);
delete[] locks;
}
template <int mr, int nr>
void there_will_be_blocks(long m0, volatile long mc, long m, long n0, long nc,
long n, long k0, long kc, long k) {
#pragma omp parallel for collapse(2) if (ops > THRESHOLD && mc * kc > 16)
for (long ic = m0; ic < m; ic += mc)
for (long pc = k0; pc < k; pc += kc)
gizmo<mr, nr>(m0, mc, ic, n0, nc, k0, kc, pc, n);
}
template <int mr, int nr>
PORTABLE OPTIMIZED void gizmo(long m0, long mc, long ic, long n0, long nc,
long k0, long kc, long pc, long n) {
T Ac[mc / mr][kc][mr];
for (long i = 0; i < mc; ++i)
for (long j = 0; j < kc; ++j)
Ac[i / mr][j][i % mr] = α * (aT ? A[lda * (pc + j) + (ic + i)]
: A[lda * (ic + i) + (pc + j)]);
for (long jc = n0; jc < n; jc += nc) {
T Bc[nc / nr][nr][kc];
for (long j = 0; j < nc; ++j)
for (long i = 0; i < kc; ++i)
Bc[j / nr][j % nr][i] =
bT ? B[ldb * (jc + j) + (pc + i)] : B[ldb * (pc + i) + (jc + j)];
T Cc[nc / nr][mc / mr][nr][mr];
memset(Cc, 0, nc * mc * sizeof(float));
for (long jr = 0; jr < nc / nr; ++jr)
for (long ir = 0; ir < mc / mr; ++ir)
for (long pr = 0; pr < kc; ++pr)
for (long j = 0; j < nr; ++j)
for (long i = 0; i < mr; ++i)
Cc[jr][ir][j][i] += Ac[ir][pr][i] * Bc[jr][j][pr];
const long lk = nl * ((ic - m0) / mc) + ((jc - n0) / nc);
locks[lk].acquire();
for (long ir = 0; ir < mc; ir += mr)
for (long jr = 0; jr < nc; jr += nr)
for (long i = 0; i < mr; ++i)
for (long j = 0; j < nr; ++j)
C[ldc * (ic + ir + i) + (jc + jr + j)] +=
Cc[jr / nr][ir / mr][j][i];
locks[lk].release();
}
}
inline long rounddown(long x, long r) {
if (x < r)
return x;
else
return x & -r;
}
class lock {
public:
lock() = default;
void acquire() {
while (lock_.exchange(true, std::memory_order_acquire)) {
}
}
void release() {
lock_.store(false, std::memory_order_release);
}
private:
std::atomic_bool lock_ = false;
};
bool aT;
bool bT;
float α;
const TA *A;
long lda;
const TB *B;
long ldb;
float β;
TC *C;
long ldc;
long ops;
long nl;
long ml;
lock *locks;
long cub;
};
template <typename TC, typename TA, typename TB>
void sgemm(bool aT, bool bT, long m, long n, long k, float α, const TA *A,
long lda, const TB *B, long ldb, float β, TC *C, long ldc) {
Gemmlin<float, TC, TA, TB> g{aT, bT, α, A, lda, B, ldb, β, C, ldc};
g.gemm(m, n, k);
}
template <typename TA, typename TB>
void show(FILE *f, long max, long m, long n, const TA *A, long lda, const TB *B,
long ldb) {
flockfile(f);
fprintf(f, " ");
for (long j = 0; j < n; ++j) {
fprintf(f, "%13ld", j);
}
fprintf(f, "\n");
for (long i = 0; i < m; ++i) {
if (i == max) {
fprintf(f, "...\n");
break;
}
fprintf(f, "%5ld ", i);
for (long j = 0; j < n; ++j) {
if (j == max) {
fprintf(f, " ...");
break;
}
char ba[16], bb[16];
sprintf(ba, "%13.7f", static_cast<double>(A[lda * i + j]));
sprintf(bb, "%13.7f", static_cast<double>(B[ldb * i + j]));
for (long k = 0; ba[k] && bb[k]; ++k) {
if (ba[k] != bb[k])
fputs_unlocked("\33[31m", f);
fputc_unlocked(ba[k], f);
if (ba[k] != bb[k])
fputs_unlocked("\33[0m", f);
}
}
fprintf(f, "\n");
}
funlockfile(f);
}
inline unsigned long GetDoubleBits(double f) {
union {
double f;
unsigned long i;
} u;
u.f = f;
return u.i;
}
inline bool IsNan(double x) {
return (GetDoubleBits(x) & (-1ull >> 1)) > (0x7ffull << 52);
}
template <typename TA, typename TB>
double diff(long m, long n, const TA *Want, long lda, const TB *Got, long ldb) {
double s = 0;
int got_nans = 0;
int want_nans = 0;
for (long i = 0; i < m; ++i)
for (long j = 0; j < n; ++j)
if (IsNan(Want[ldb * i + j]))
++want_nans;
else if (IsNan(Got[ldb * i + j]))
++got_nans;
else
s += std::fabs(Want[lda * i + j] - Got[ldb * i + j]);
if (got_nans)
printf("WARNING: got %d NaNs!\n", got_nans);
if (want_nans)
printf("WARNING: want array has %d NaNs!\n", want_nans);
return s / (m * n);
}
template <typename TA, typename TB>
void show_error(FILE *f, long max, long m, long n, const TA *A, long lda,
const TB *B, long ldb, const char *file, int line, double sad,
double tol) {
fprintf(f, "%s:%d: sad %.17g exceeds %g\nwant\n", file, line, sad, tol);
show(f, max, m, n, A, lda, B, ldb);
fprintf(f, "got\n");
show(f, max, m, n, B, ldb, A, lda);
fprintf(f, "\n");
}
template <typename TA, typename TB>
void check(double tol, long m, long n, const TA *A, long lda, const TB *B,
long ldb, const char *file, int line) {
double sad = diff(m, n, A, lda, B, ldb);
if (sad <= tol) {
if (!is_self_testing) {
printf(" %g error\n", sad);
}
} else {
show_error(stderr, 16, m, n, A, lda, B, ldb, file, line, sad, tol);
const char *path = "/tmp/openmp_test.log";
FILE *f = fopen(path, "w");
if (f) {
show_error(f, 10000, m, n, A, lda, B, ldb, file, line, sad, tol);
printf("see also %s\n", path);
}
exit(1);
}
}
#define check(tol, m, n, A, lda, B, ldb) \
check(tol, m, n, A, lda, B, ldb, __FILE__, __LINE__)
long micros(void) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
return ts.tv_sec * 1000000 + (ts.tv_nsec + 999) / 1000;
}
#define bench(x) \
do { \
long t1 = micros(); \
for (long i = 0; i < ITERATIONS; ++i) { \
asm volatile("" ::: "memory"); \
x; \
asm volatile("" ::: "memory"); \
} \
long t2 = micros(); \
printf("%8" PRId64 " µs %s\n", (t2 - t1 + ITERATIONS - 1) / ITERATIONS, \
#x); \
} while (0)
double real01(unsigned long x) { // (0,1)
return 1. / 4503599627370496. * ((x >> 12) + .5);
}
double numba(void) { // (-1,1)
return real01(lemur64()) * 2 - 1;
}
template <typename T>
void fill(T *A, long n) {
for (long i = 0; i < n; ++i) {
A[i] = numba();
}
}
void test_gemm(long m, long n, long k) {
float *A = new float[m * k];
float *At = new float[k * m];
float *B = new float[k * n];
float *Bt = new float[n * k];
float *C = new float[m * n];
float *GOLD = new float[m * n];
float α = 1;
float β = 0;
fill(A, m * k);
fill(B, k * n);
dgemm(0, 0, m, n, k, 1, A, k, B, n, 0, GOLD, n);
transpose(m, k, A, k, At, m);
transpose(k, n, B, n, Bt, k);
sgemm(0, 0, m, n, k, α, A, k, B, n, β, C, n);
check(PRECISION, m, n, GOLD, n, C, n);
sgemm(1, 0, m, n, k, α, At, m, B, n, β, C, n);
check(PRECISION, m, n, GOLD, n, C, n);
sgemm(0, 1, m, n, k, α, A, k, Bt, k, β, C, n);
check(PRECISION, m, n, GOLD, n, C, n);
sgemm(1, 1, m, n, k, α, At, m, Bt, k, β, C, n);
check(PRECISION, m, n, GOLD, n, C, n);
delete[] GOLD;
delete[] C;
delete[] Bt;
delete[] B;
delete[] At;
delete[] A;
}
void check_gemm_works(void) {
static long kSizes[] = {1, 2, 3, 4, 5, 6, 7, 17, 31, 33, 63, 128, 129};
is_self_testing = true;
long c = 0;
long N = sizeof(kSizes) / sizeof(kSizes[0]);
for (long i = 0; i < N; ++i) {
long m = kSizes[i];
for (long j = 0; j < N; ++j) {
long n = kSizes[N - 1 - i];
for (long k = 0; k < N; ++k) {
long K = kSizes[i];
if (c++ % 13 == 0) {
printf("testing %2ld %2ld %2ld\r", m, n, K);
}
test_gemm(m, n, K);
}
}
}
printf("\r");
is_self_testing = false;
}
long m = 2333 / 3;
long k = 577 / 3;
long n = 713 / 3;
void check_sgemm(void) {
float *A = new float[m * k];
float *At = new float[k * m];
float *B = new float[k * n];
float *Bt = new float[n * k];
float *C = new float[m * n];
double *GOLD = new double[m * n];
fill(A, m * k);
fill(B, k * n);
transpose(m, k, A, k, At, m);
transpose(k, n, B, n, Bt, k);
bench(dgemm(0, 0, m, n, k, 1, A, k, B, n, 0, GOLD, n));
bench(sgemm(0, 0, m, n, k, 1, A, k, B, n, 0, C, n));
check(PRECISION, m, n, GOLD, n, C, n);
bench(sgemm(1, 0, m, n, k, 1, At, m, B, n, 0, C, n));
check(PRECISION, m, n, GOLD, n, C, n);
bench(sgemm(0, 1, m, n, k, 1, A, k, Bt, k, 0, C, n));
check(PRECISION, m, n, GOLD, n, C, n);
bench(sgemm(1, 1, m, n, k, 1, At, m, Bt, k, 0, C, n));
check(PRECISION, m, n, GOLD, n, C, n);
delete[] GOLD;
delete[] C;
delete[] Bt;
delete[] B;
delete[] At;
delete[] A;
}
int main(int argc, char *argv[]) {
check_gemm_works();
check_sgemm();
}

View file

@ -20,7 +20,6 @@ o/$(MODE)/third_party: \
o/$(MODE)/third_party/libcxxabi \
o/$(MODE)/third_party/libunwind \
o/$(MODE)/third_party/linenoise \
o/$(MODE)/third_party/llm \
o/$(MODE)/third_party/lua \
o/$(MODE)/third_party/lz4cli \
o/$(MODE)/third_party/make \

1
third_party/awk/b.c vendored
View file

@ -1463,6 +1463,7 @@ rescan:
}
break;
}
__builtin_unreachable();
}
int cgoto(fa *f, int s, int c)

View file

@ -45,9 +45,9 @@ __static_yoink("musl_libc_notice");
// something as substantive as this library, then we shall assume the
// application is meaty enough to benefit from the performance of the
// chromium zlib library (costs ~40kb binary) versus just having puff
__static_yoink("_Cz_inflateInit2");
__static_yoink("_Cz_inflate");
__static_yoink("_Cz_inflateEnd");
__static_yoink("inflateInit2");
__static_yoink("inflate");
__static_yoink("inflateEnd");
#endif
static char *

View file

@ -31,7 +31,7 @@
#ifndef USE_ZLIB
ZCONST ulg near *get_crc_table OF((void));
#endif
#if (defined(USE_ZLIB) || defined(CRC_TABLE_ONLY))
#if (1 || defined(USE_ZLIB) || defined(CRC_TABLE_ONLY))
# ifdef IZ_CRC_BE_OPTIMIZ
# undef IZ_CRC_BE_OPTIMIZ
# endif

View file

@ -72,7 +72,7 @@
#include "libc/nt/winsock.h"
#endif
unsigned _Cz_crc32(unsigned crc, const unsigned char *buf, unsigned len);
unsigned crc32(unsigned crc, const unsigned char *buf, unsigned len);
/*
* XXX start of zipfile.h
@ -867,7 +867,7 @@ local void read_Unicode_Path_entry(pZipListEntry)
}
strcpy(iname, pZipListEntry->iname);
chksum = _Cz_crc32(chksum, (uch *)(iname), strlen(iname));
chksum = crc32(chksum, (uch *)(iname), strlen(iname));
free(iname);
@ -972,7 +972,7 @@ local void read_Unicode_Path_local_entry(pZipListEntry)
}
strcpy(iname, pZipListEntry->iname);
chksum = _Cz_crc32(chksum, (uch *)(iname), strlen(iname));
chksum = crc32(chksum, (uch *)(iname), strlen(iname));
free(iname);
@ -1558,7 +1558,7 @@ local int add_Unicode_Path_local_extra_field(pZEntry)
# define inameLocal (pZEntry->iname)
#endif
chksum = _Cz_crc32(chksum, (uch *)(inameLocal), strlen(inameLocal));
chksum = crc32(chksum, (uch *)(inameLocal), strlen(inameLocal));
#ifdef WIN32_OEM
free(inameLocal);
@ -1685,7 +1685,7 @@ local int add_Unicode_Path_cen_extra_field(pZEntry)
# define inameLocal (pZEntry->iname)
#endif
chksum = _Cz_crc32(chksum, (uch *)(inameLocal), strlen(inameLocal));
chksum = crc32(chksum, (uch *)(inameLocal), strlen(inameLocal));
#ifdef WIN32_OEM
free(inameLocal);

View file

@ -13,6 +13,7 @@
#define z_const const
#if 0
#define Z_COSMO_PREFIX_SET
#define Bytef _Cz_Bytef
@ -162,6 +163,9 @@
#define zlibCompileFlags _Cz_zlibCompileFlags
#define zlibVersion _Cz_zlibVersion
#pragma message "zconf is included, so zlibVersion should be renamed"
#endif
typedef unsigned char Byte;
typedef unsigned int uInt; /* 16 bits or more */

View file

@ -21,10 +21,10 @@ AMD64=${2:-x86_64}
ARM64=${3:-aarch64}
GCCVER=12.3.0
make -j32 m= \
make -j64 m= \
$APELINK
make -j32 m=$AMD64 \
make -j64 m=$AMD64 \
o/cosmocc.h.txt \
o/$AMD64/ape/ape.lds \
o/$AMD64/libc/crt/crt.o \
@ -47,7 +47,7 @@ make -j32 m=$AMD64 \
o/$AMD64/third_party/make/make.dbg \
o/$AMD64/third_party/ctags/ctags.dbg
make -j32 m=$ARM64 \
make -j64 m=$ARM64 \
o/$ARM64/ape/ape.elf \
o/$ARM64/ape/aarch64.lds \
o/$ARM64/libc/crt/crt.o \
@ -90,10 +90,10 @@ fetch() {
OLD=$PWD
cd "$OUTDIR/"
if [ ! -x bin/x86_64-linux-cosmo-gcc ]; then
fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.35/aarch64-gcc.zip
fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.39/aarch64-gcc.zip
unzip aarch64-gcc.zip
rm -f aarch64-gcc.zip
fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.35/x86_64-gcc.zip
fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.39/x86_64-gcc.zip
unzip x86_64-gcc.zip
rm -f x86_64-gcc.zip
fi

View file

@ -215,7 +215,7 @@
(runs (format "o/$m/%s%s V=5 TESTARGS=-b" name runsuffix))
(buns (format "o/$m/test/%s_test%s V=5 TESTARGS=-b" name runsuffix)))
(cond ((not (member ext '("c" "cc" "cpp" "s" "S" "rl" "f" "cu")))
(format "m=%s; make -j32 MODE=$m o/$m/%s"
(format "m=%s; make -j96 MODE=$m o/$m/%s"
mode
(directory-file-name
(or (file-name-directory
@ -226,7 +226,7 @@
(cosmo-join
" && "
`("m=%s; f=o/$m/%s"
,(concat "make -j32 $f MODE=$m")
,(concat "make -j96 $f MODE=$m")
"scp $f $f.dbg win10:; ssh win10 ./%s"))
mode name (file-name-nondirectory name)))
((eq kind 'run-xnu)
@ -234,19 +234,19 @@
(cosmo-join
" && "
`("m=%s; f=o/$m/%s"
,(concat "make -j32 $f MODE=$m")
,(concat "make -j96 $f MODE=$m")
"scp $f $f.dbg xnu:"
"ssh xnu ./%s"))
mode name (file-name-nondirectory name)))
((and (equal suffix "")
(cosmo-contains "_test." (buffer-file-name)))
(format "m=%s; make -j32 MODE=$m %s"
(format "m=%s; make -j96 MODE=$m %s"
mode runs))
((and (equal suffix "")
(file-exists-p (format "%s" buddy)))
(format (cosmo-join
" && "
'("m=%s; n=%s; make -j32 o/$m/$n%s.o MODE=$m"
'("m=%s; n=%s; make -j96 o/$m/$n%s.o MODE=$m"
;; "bloat o/$m/%s.o | head"
;; "nm -C --size o/$m/%s.o | sort -r"
"echo"
@ -258,11 +258,11 @@
(cosmo-join
" && "
`("m=%s; f=o/$m/%s"
,(concat "make -j32 $f MODE=$m")
,(concat "make -j96 $f MODE=$m")
"build/run ./$f"))
mode name))
((eq kind 'test)
(format `"m=%s; f=o/$m/%s.ok && make -j32 $f MODE=$m" mode name))
(format `"m=%s; f=o/$m/%s.ok && make -j96 $f MODE=$m" mode name))
((and (file-regular-p this)
(file-executable-p this))
(format "build/run ./%s" file))
@ -271,7 +271,7 @@
(cosmo-join
" && "
`("m=%s; f=o/$m/%s%s.o"
,(concat "make -j32 $f MODE=$m")
,(concat "make -j96 $f MODE=$m")
;; "nm -C --size $f | sort -r"
"echo"
"size -A $f | grep '^[.T]' | grep -v 'debug\\|command.line\\|stack' | sort -rnk2"
@ -481,7 +481,7 @@
(error "don't know how to show assembly for non c/c++ source file"))
(let* ((default-directory root)
(compile-command
(format "make %s -j32 MODE=%s %s %s"
(format "make %s -j96 MODE=%s %s %s"
(or extra-make-flags "") mode asm-gcc asm-clang)))
(save-buffer)
(set-visited-file-modtime (current-time))
@ -641,11 +641,11 @@
(compile (format "sh -c %s" file)))
((eq major-mode 'lua-mode)
(let* ((mode (cosmo--make-mode arg)))
(compile (format "make -j32 MODE=%s o/%s/tool/net/redbean && build/run o/%s/tool/net/redbean -i %s" mode mode mode file))))
(compile (format "make -j96 MODE=%s o/%s/tool/net/redbean && build/run o/%s/tool/net/redbean -i %s" mode mode mode file))))
((and (eq major-mode 'python-mode)
(cosmo-startswith "third_party/python/Lib/test/" file))
(let ((mode (cosmo--make-mode arg)))
(compile (format "make -j32 MODE=%s PYHARNESSARGS=-vv PYTESTARGS=-v o/%s/%s.py.runs"
(compile (format "make -j96 MODE=%s PYHARNESSARGS=-vv PYTESTARGS=-v o/%s/%s.py.runs"
mode mode (file-name-sans-extension file)))))
((eq major-mode 'python-mode)
(compile (format "python %s" file)))