Skip to content

Instantly share code, notes, and snippets.

@skeeto
Created January 10, 2024 02:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skeeto/f92daacf86ed81e2d097ab668ed3117b to your computer and use it in GitHub Desktop.
Save skeeto/f92daacf86ed81e2d097ab668ed3117b to your computer and use it in GitHub Desktop.
/proc/meminfo parser
// Parse /proc/meminfo into a hash map
// This is free and unencumbered software released into the public domain.
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define S(s) (str){s, sizeof(s)-1}
#define new(a, t) (t *)alloc(a, sizeof(t))
typedef struct {
char *beg, *end;
} arena;
static void *alloc(arena *a, ptrdiff_t size)
{
if (size > a->end-a->beg) {
abort(); // out of memory
}
return memset(a->end -= size, 0, size);
}
typedef struct {
char *data;
ptrdiff_t len;
} str;
static _Bool equals(str a, str b)
{
return a.len==b.len && (!a.len || !memcmp(a.data, b.data, a.len));
}
static uint64_t hash64(str s)
{
uint64_t h = 0x100;
for (ptrdiff_t i = 0; i < s.len; i++) {
h ^= s.data[i] & 255;
h *= 1111111111111111111u;
}
return h;
}
// Read entire file into a string.
static str readfile(char *path, arena *perm)
{
str s = {0};
int fd = open(path, O_RDONLY);
if (fd >= 0) {
s.data = perm->beg;
ptrdiff_t cap = perm->end - perm->beg;
while (s.len < cap) {
ptrdiff_t r = read(fd, s.data+s.len, cap-s.len);
if (r < 1) {
break;
}
s.len += r;
}
perm->beg += s.len;
close(fd);
}
return s;
}
typedef struct {
str head;
str tail;
} cut;
// Cut a string into two pieces around a delimiter.
static cut cutstr(str s, char delim)
{
// skip leading matches
for (; s.len && *s.data==delim; s.data++, s.len--) {}
// find next delimiter, or end of string
ptrdiff_t len = 0;
for (; len<s.len && s.data[len]!=delim; len++) {}
cut r = {0};
r.tail = r.head = s;
r.head.len = len;
len += len < s.len; // exclude delimiter
r.tail.data += len;
r.tail.len -= len;
return r;
}
typedef struct meminfo meminfo;
struct meminfo {
meminfo *child[2];
str key;
int64_t value;
};
meminfo *newmeminfo(arena *perm)
{
cut line = {0};
line.tail = readfile("/proc/meminfo", perm);
meminfo *mi = 0;
while (line.tail.len) {
cut token = {0};
line = cutstr(line.tail, '\n');
token.tail = line.head;
token = cutstr(token.tail, ' ');
str key = token.head;
token = cutstr(token.tail, ' ');
str value = token.head;
token = cutstr(token.tail, ' ');
str units = token.head;
if (!key.len) {
continue; // bad input? skip line
}
key.len--; // chop trailing ':'
uint64_t v = 0; // allow for overflow
for (ptrdiff_t i = 0; i < value.len; i++) {
v = v*10 + value.data[i] - '0';
}
if (equals(units, S("kB"))) {
v *= 1024;
}
meminfo **m = &mi;
for (uint64_t h = hash64(key); *m; h <<= 1) {
m = &(*m)->child[h>>63];
}
*m = new(perm, meminfo);
(*m)->key = key;
(*m)->value = v;
}
return mi;
}
int64_t lookup(meminfo *mi, str key)
{
for (uint64_t h = hash64(key); mi; h <<= 1) {
if (equals(key, mi->key)) {
return mi->value;
}
mi = mi->child[h>>63];
}
return -1;
}
static arena newarena(ptrdiff_t cap)
{
arena a = {0};
a.beg = malloc(cap);
a.end = a.beg ? a.beg+cap : a.beg;
return a;
}
int main(void)
{
arena scratch = newarena(1<<20);
meminfo *mi = newmeminfo(&scratch);
int64_t swaptotal = lookup(mi, S("SwapTotal"));
printf("SwapTotal = %lld kB\n", (long long)swaptotal/1024);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment