commit 5108dd9f1b9b187376b3744e72cc729a18c59936
parent 83d715acb180f4fd7189cc8de00a8889a36abe6d
Author: Henry Wilson <henry@henryandlizzy.uk>
Date: Thu, 12 Mar 2026 15:55:32 +0000
Squash cpponline branch (only code, no slides/docs)
from: 3df49732f2bb3a4a32211ad9d7d6af585f682ed8
Diffstat:
16 files changed, 118 insertions(+), 66 deletions(-)
diff --git a/Tupfile b/Tupfile
@@ -21,6 +21,4 @@ LDFLAGS = --gc-sections
run ./gen.sh
-: bin/writer.aarch64 |> %f |>
-
.gitignore
diff --git a/cat.cpp b/cat.cpp
@@ -1,6 +1,6 @@
#include "linux.hpp"
-static void cat(file fd)
+static void cat(fd fd)
{
static char buf[0x1000];
diff --git a/errno.sh b/errno.sh
@@ -4,3 +4,7 @@ set -eu
echo -e '#pragma once\n\nenum class errno_t : int16_t {'
sed -En 's/#[[:space:]]*define[[:space:]]+E([[:alnum:]]+)[[:space:]]+([[:digit:]]+)/E\1 = -\2,/p' /usr/include/asm-generic/errno{,-base}.h
echo '};'
+
+echo -e 'inline span<char const> errname(errno_t e) { switch (e) {'
+sed -En 's/#[[:space:]]*define[[:space:]]+E([[:alnum:]]+)[[:space:]]+([[:digit:]]+)/case errno_t::E\1: return "E\1"_sp;/p' /usr/include/asm-generic/errno{,-base}.h
+echo '} return "E?unknown?"_sp; }'
diff --git a/exec.cpp b/exec.cpp
@@ -1,5 +1,4 @@
#include "linux.hpp"
-#include "errno.hpp"
int main(int argc, char* argv[], char* envp[])
{
diff --git a/gen.sh b/gen.sh
@@ -3,3 +3,4 @@
ARCH=`uname -m`
echo ": foreach {bin-$ARCH} |> ln -s %f %o |> %B"
+echo ": bin/writer.$ARCH |> %f |>"
diff --git a/hello.cpp b/hello.cpp
@@ -0,0 +1,24 @@
+#include "linux.hpp"
+
+int main()
+{
+ auto msg = "Hello, World!\n"_sp;
+ auto res = write(stdout, msg);
+
+ if (not res)
+ {
+ span<char const> const iov[] = {
+ "write() failed: "_sp,
+ errname(res.err()),
+ "\n"_sp
+ };
+ (void)write(stderr, span{iov});
+ return 1;
+ }
+
+ if (*res != msg.size())
+ {
+ (void)write(stderr, "Wrote less than expected\n"_sp);
+ return 1;
+ }
+}
diff --git a/linux.hpp b/linux.hpp
@@ -23,6 +23,7 @@ enum {
O_WRONLY = 1,
O_RDWR = 2,
O_CREAT = 0100,
+ O_PATH = 010000000,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -48,7 +49,7 @@ enum {
template <typename T>
struct span
{
- span() = default;
+ constexpr span() = default;
constexpr span(T* p, size_t n) noexcept
: ptr{p}
, len{n}
@@ -103,20 +104,21 @@ constexpr span<char const> operator ""_sp(char const* begin, size_t size) noexce
return {begin, size};
}
-struct file
+#include "errno.hpp"
+
+struct fd
{
- constexpr file() = default;
- constexpr explicit file(int n) noexcept : num{n} {}
+ constexpr fd() = default;
+ constexpr explicit fd(int n) noexcept : num{n} {}
constexpr operator bool() const noexcept { return num >= 0; }
+private:
int num = -1;
};
-constexpr file const AT_FDCWD{-100};
-constexpr file const stdin{0};
-constexpr file const stdout{1};
-constexpr file const stderr{2};
-
-enum class errno_t : int16_t;
+constexpr fd const AT_FDCWD{-100};
+constexpr fd const stdin{0};
+constexpr fd const stdout{1};
+constexpr fd const stderr{2};
template <typename T>
struct [[nodiscard]] syscall_result
@@ -173,24 +175,24 @@ private:
[[noreturn]]
extern void exit(int error_code) noexcept;
-extern syscall_result<size_t> read(file fd, char data[], size_t count) noexcept;
-extern syscall_result<size_t> read(file fd, span<char> data) noexcept;
-inline syscall_result<size_t> read(file fd, char* begin, char* end) noexcept
+extern syscall_result<size_t> read(fd src, char data[], size_t count) noexcept;
+extern syscall_result<size_t> read(fd src, span<char> data) noexcept;
+inline syscall_result<size_t> read(fd src, char* begin, char* end) noexcept
{
- return read(fd, begin, end - begin);
+ return read(src, begin, end - begin);
}
-extern syscall_result<size_t> write(file fd, char const data[], size_t count) noexcept;
-extern syscall_result<size_t> write(file fd, span<char const> data) noexcept;
-extern syscall_result<size_t> write(file fd, span<span<char const> const> data) noexcept;
-inline syscall_result<size_t> write(file fd, char const* begin, char const* end) noexcept
+extern syscall_result<size_t> write(fd dst, char const data[], size_t count) noexcept;
+extern syscall_result<size_t> write(fd dst, span<char const> data) noexcept;
+extern syscall_result<size_t> write(fd dst, span<span<char const> const> data) noexcept;
+inline syscall_result<size_t> write(fd dst, char const* begin, char const* end) noexcept
{
- return write(fd, begin, end - begin);
+ return write(dst, begin, end - begin);
}
-extern syscall_result<file> openat(file fd, c_str name, int flags, int mode) noexcept;
+extern syscall_result<fd> openat(fd dir, c_str name, int flags, int mode) noexcept;
-extern syscall_result<void> close(file fd) noexcept;
+extern syscall_result<void> close(fd) noexcept;
struct [[gnu::packed]] linux_dirent64 {
ino64_t d_ino; /* 64-bit inode number */
@@ -201,9 +203,9 @@ struct [[gnu::packed]] linux_dirent64 {
linux_dirent64* next() noexcept { return reinterpret_cast<linux_dirent64*>(reinterpret_cast<char*>(this) + d_reclen); }
char* name() noexcept { return reinterpret_cast<char*>(this) + sizeof(*this); }
};
-extern syscall_result<size_t> getdents64(file dir, span<char> buffer) noexcept;
+extern syscall_result<size_t> getdents64(fd dir, span<char> buffer) noexcept;
-extern syscall_result<void*> mmap(void* addr, size_t len, unsigned long prot, unsigned long flags, file fd, size_t off) noexcept;
+extern syscall_result<void*> mmap(void* addr, size_t len, unsigned long prot, unsigned long flags, fd file, size_t off) noexcept;
extern syscall_result<void> munmap(void* addr, size_t len) noexcept;
struct timespec {
@@ -220,18 +222,18 @@ enum class rename_flags : unsigned int
WHITEOUT = 0b100,
};
-extern syscall_result<void> renameat2(file olddir, c_str oldpath, file newdir, c_str newpath, rename_flags flags) noexcept;
+extern syscall_result<void> renameat2(fd olddir, c_str oldpath, fd newdir, c_str newpath, rename_flags flags) noexcept;
enum class unlink_flags : int
{
REMOVEDIR = 0x200,
};
-extern syscall_result<void> unlinkat(file dir, c_str path, unlink_flags flags) noexcept;
+extern syscall_result<void> unlinkat(fd dir, c_str path, unlink_flags flags) noexcept;
extern syscall_result<void> execve(c_str path, char *const /*_Nullable*/ argv[], char *const /*_Nullable*/ envp[]) noexcept;
-extern syscall_result<file> socket(int domain, int type, int protocol) noexcept;
+extern syscall_result<fd> socket(int domain, int type, int protocol) noexcept;
struct addr_ref
{
@@ -276,8 +278,8 @@ private:
[[maybe_unused]] uint8_t data[14];
};
-extern syscall_result<void> bind(file socket, addr_ref addr) noexcept;
-extern syscall_result<void> listen(file socket, int backlog) noexcept;
-extern syscall_result<file> accept(file socket, void* addr_buf, socklen_t* addr_len) noexcept;
-syscall_result<void> setsockopt(file socket, int level, int option_name, span<char const> value);
-extern syscall_result<file> dup(file oldfd, file newfd, int flags = 0) noexcept;
+extern syscall_result<void> bind(fd socket, addr_ref addr) noexcept;
+extern syscall_result<void> listen(fd socket, int backlog) noexcept;
+extern syscall_result<fd> accept(fd socket, void* addr_buf, socklen_t* addr_len) noexcept;
+syscall_result<void> setsockopt(fd socket, int level, int option_name, span<char const> value);
+extern syscall_result<fd> dup(fd oldfd, fd newfd, int flags = 0) noexcept;
diff --git a/paste.cpp b/paste.cpp
@@ -2,7 +2,7 @@
struct input
{
- file fd;
+ fd fd;
unsigned short start, end;
char buf[0x1000 - 8];
};
diff --git a/syscalls.h b/syscalls.h
@@ -1,31 +1,31 @@
extern unlinkat
-extern_alias _Z8unlinkat4file5c_str12unlink_flags
+extern_alias _Z8unlinkat2fd5c_str12unlink_flags
SYS(unlinkat)
extern getdents64
-extern_alias _Z10getdents644file4spanIcE
+extern_alias _Z10getdents642fd4spanIcE
SYS(getdents64)
extern read
-extern_alias _Z4read4filePcm
-extern_alias _Z4read4file4spanIcE
+extern_alias _Z4read2fdPcm
+extern_alias _Z4read2fd4spanIcE
SYS(read)
extern write
-extern_alias _Z5write4filePKcm
-extern_alias _Z5write4file4spanIKcE
+extern_alias _Z5write2fdPKcm
+extern_alias _Z5write2fd4spanIKcE
SYS(write)
extern openat
-extern_alias _Z6openat4file5c_strii
+extern_alias _Z6openat2fd5c_strii
SYS(openat)
extern writev
-extern_alias _Z5write4file4spanIKS0_IKcEE
+extern_alias _Z5write2fd4spanIKS0_IKcEE
SYS(writev)
extern close
-extern_alias _Z5close4file
+extern_alias _Z5close2fd
SYS(close)
extern execve
@@ -45,11 +45,11 @@ extern_alias _Z6munmapPvm
SYS(munmap)
extern mmap
-extern_alias _Z4mmapPvmmm4filem
+extern_alias _Z4mmapPvmmm2fdm
SYS(mmap)
extern renameat2
-extern_alias _Z9renameat24file5c_strS_S0_12rename_flags
+extern_alias _Z9renameat22fd5c_strS_S0_12rename_flags
SYS(renameat2)
extern socket
@@ -57,21 +57,21 @@ extern_alias _Z6socketiii
SYS(socket)
extern bind
-extern_alias _Z4bind4file8addr_ref
+extern_alias _Z4bind2fd8addr_ref
SYS(bind)
extern listen
-extern_alias _Z6listen4filei
+extern_alias _Z6listen2fdi
SYS(listen)
extern accept
-extern_alias _Z6accept4filePvPj
+extern_alias _Z6accept2fdPvPj
SYS(accept)
extern dup3
-extern_alias _Z3dup4fileS_i
+extern_alias _Z3dup2fdS_i
SYS(dup3)
extern setsockopt
-extern_alias _Z10setsockopt4fileii4spanIKcE
+extern_alias _Z10setsockopt2fdii4spanIKcE
SYS(setsockopt)
diff --git a/tac.cpp b/tac.cpp
@@ -2,7 +2,7 @@
extern char mappable[];
-static int rd(file const fd, char* const start, char* const end)
+static int rd(fd const fd, char* const start, char* const end)
{
char* pos = start;
while (pos != end)
@@ -18,7 +18,7 @@ static int rd(file const fd, char* const start, char* const end)
return pos - start;
}
-static void wr(file const fd, char const* start, char const* const end)
+static void wr(fd const fd, char const* start, char const* const end)
{
while (start != end)
{
diff --git a/tcp-listen.cpp b/tcp-listen.cpp
@@ -82,12 +82,12 @@ int main(int argc, char* argv[], char* envp[])
if (port > 65535)
return 3;
- file sock = *socket(2/*AF_INET*/, 1/*SOCK_STREAM*/, 0/*IPPROTO_IP*/);
+ fd sock = *socket(2/*AF_INET*/, 1/*SOCK_STREAM*/, 0/*IPPROTO_IP*/);
in_addr addr{static_cast<uint16_t>(port), 0/*IN_ADDR_ANY*/};
*bind(sock, addr);
*listen(sock, 1);
socklen_t addr_len = sizeof(addr);
- file conn = *accept(sock, &addr, &addr_len);
+ fd conn = *accept(sock, &addr, &addr_len);
*dup(conn, stdin);
*dup(conn, stdout);
*close(conn);
diff --git a/tee.cpp b/tee.cpp
@@ -3,7 +3,7 @@
int main(int argc, char* argv[])
{
- vector<file> files;
+ vector<fd> files;
for (int i = 1; i < argc; ++i)
files.push_back(*openat(AT_FDCWD, argv[i], O_WRONLY | O_CREAT, 0666));
diff --git a/unix-listen.cpp b/unix-listen.cpp
@@ -7,12 +7,12 @@ int main(int argc, char* argv[], char* envp[])
(void)unlinkat(AT_FDCWD, argv[1], {});
- file sock = *socket(1/*AF_UNIX*/, 1/*SOCK_STREAM*/, 0);
+ fd sock = *socket(1/*AF_UNIX*/, 1/*SOCK_STREAM*/, 0);
un_addr addr{span<char const>{argv[1], c_str{argv[1]}.length()}};
*bind(sock, addr);
*listen(sock, 1);
socklen_t addr_len = sizeof(addr);
- file conn = *accept(sock, &addr, &addr_len);
+ fd conn = *accept(sock, &addr, &addr_len);
*dup(conn, stdin);
*dup(conn, stdout);
*close(conn);
diff --git a/vector.hpp b/vector.hpp
@@ -6,12 +6,37 @@ inline void* operator new(size_t, void* p) noexcept
return p;
}
+struct false_type { static constexpr bool value = false; };
+struct true_type { static constexpr bool value = true; };
+
+template<typename T, typename U> struct is_same : false_type {};
+template<typename T> struct is_same<T, T> : true_type {};
+template<typename T, typename U> constexpr bool is_same_v = is_same<T, U>::value;
+
+template <typename T>
+consteval unsigned bits()
+{
+ if constexpr (is_same_v<T, uint64_t>)
+ return 64;
+ if constexpr (is_same_v<T, int64_t>)
+ return 64;
+ if constexpr (is_same_v<T, uint32_t>)
+ return 32;
+ if constexpr (is_same_v<T, int32_t>)
+ return 32;
+}
+
+static_assert(bits<int64_t>() == 64);
+static_assert(bits<uint64_t>() == 64);
+static_assert(bits<int32_t>() == 32);
+static_assert(bits<uint32_t>() == 32);
+
constexpr size_t next_power_of_two(size_t n)
{
if (not n)
return n;
--n;
- for (unsigned i = 1; i <= 32; i <<= 1)
+ for (unsigned i = 1; i <= bits<size_t>(); i <<= 1)
n |= n >> i;
return ++n;
}
diff --git a/writer.cpp b/writer.cpp
@@ -13,14 +13,14 @@ protected:
struct fd_writer : writer
{
- fd_writer(file f) : out{f} {}
+ fd_writer(fd f) : out{f} {}
void write(span<char const> data) override
{
assert(*::write(out, data) == data.size());
}
private:
- file out;
+ fd out;
};
struct buf_writer : writer
@@ -42,11 +42,12 @@ private:
void decimal(writer& w, uint64_t n)
{
char buf[20];
- char* const end = buf + 16;
+ char* const end = buf + sizeof(buf);
char* p = end;
do
{
- *--p = n % 10 + '0';
+ assert(--p >= buf);
+ *p = n % 10 + '0';
n /= 10;
} while (n);
w.write({p, end});
diff --git a/x86_64.S b/x86_64.S
@@ -1,5 +1,3 @@
-# Syscall numbers https://www.chromium.org/chromium-os/developer-library/reference/linux-constants/syscalls/#x86_64-64-bit
-
#include "NR_x86_64.h"
.intel_syntax noprefix