Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 132 additions & 44 deletions xdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

#define _GNU_SOURCE
#include "version.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -131,11 +132,80 @@ struct node {
struct node* peer; /* siblings */
struct node* child; /* list of children if !NULL */
struct node* parent; /* backpointer to parent */
struct node* hnext; /* next in hash chain */
} top;
struct node* topp = &top;
#define NODE_NULL ((struct node*)0)
long nnodes = 0;

/*
* Hash table to find a child by (parent, name) in constant time
*/
static struct node** htab;
static size_t hsize; /* 1 << hbits */
static int hbits;
static size_t hcount;

/*
* Hash the name with 64-bit FNV-1a (RFC 9923), mix in the parent pointer
* and reduce to a bucket index of the given width with Fibonacci hashing
* (Knuth, TAOCP Vol. 3, 6.4: multiply by 2^64/phi and keep the top bits).
*/
static size_t hash(struct node* parent, char* name, int bits)
{
uint64_t h = 0xcbf29ce484222325ULL;

while (*name)
h = (h ^ (unsigned char)*name++) * 0x100000001b3ULL;
h ^= (uintptr_t)parent;
return (size_t)((h * 0x9e3779b97f4a7c15ULL) >> (64 - bits));
}

static void hinsert(struct node* np)
{
size_t i;

if (hcount >= hsize) {
int nbits = hbits ? hbits + 1 : 10;
size_t nsize = (size_t)1 << nbits;
struct node** ntab = calloc(nsize, sizeof(struct node*));
struct node *p, *next;

if (ntab == NULL) {
fprintf(stderr, "xdu: out of memory\n");
exit(1);
}
for (i = 0; i < hsize; i++) {
for (p = htab[i]; p != NODE_NULL; p = next) {
size_t j = hash(p->parent, p->name, nbits);
next = p->hnext;
p->hnext = ntab[j];
ntab[j] = p;
}
}
free(htab);
htab = ntab;
hsize = nsize;
hbits = nbits;
}
i = hash(np->parent, np->name, hbits);
np->hnext = htab[i];
htab[i] = np;
hcount++;
}

static struct node* hlookup(struct node* parent, char* name)
{
struct node* np;

if (hsize == 0)
return NODE_NULL;
for (np = htab[hash(parent, name, hbits)]; np != NODE_NULL; np = np->hnext)
if (np->parent == parent && strcmp(np->name, name) == 0)
return np;
return NODE_NULL;
}

/*
* create a new node with the given name and size info
*/
Expand Down Expand Up @@ -427,26 +497,25 @@ static void addtree(struct node* top, char* path[], long long size)
*/

/*
* check all children for a match
* check children for a match
*/
for (np = top->child; np != NULL; np = np->peer) {
if (strcmp(path[0], np->name) == 0) {
/*
* name matches
*/
if (path[1] == NULL) {
/*
* end of the chain, save size
*/
np->size = size;
return;
}
np = hlookup(top, path[0]);
if (np != NODE_NULL) {
/*
* name matches
*/
if (path[1] == NULL) {
/*
* recurse
* end of the chain, save size
*/
addtree(np, &path[1], size);
np->size = size;
return;
}
/*
* recurse
*/
addtree(np, &path[1], size);
return;
}
/*
* no child matched, add a new child
Expand All @@ -455,6 +524,7 @@ static void addtree(struct node* top, char* path[], long long size)
np->parent = top;
np->peer = top->child;
top->child=np;
hinsert(np);

if (path[1] == NULL) {
/*
Expand Down Expand Up @@ -487,13 +557,55 @@ void dumptree(struct node* np, int level)
}
}

/*
* merge sort a list of peers, return the new head
*/
static struct node* sortlist(struct node* list, int order)
{
struct node *slow, *fast, *right;
struct node* head;
struct node** tail;

if (list == NODE_NULL || list->peer == NODE_NULL)
return list;

/*
* split the list in half
*/
slow = list;
fast = list->peer;
while (fast != NODE_NULL && fast->peer != NODE_NULL) {
slow = slow->peer;
fast = fast->peer->peer;
}
right = slow->peer;
slow->peer = NODE_NULL;

list = sortlist(list, order);
right = sortlist(right, order);

/*
* merge both halves
*/
tail = &head;
while (list != NODE_NULL && right != NODE_NULL) {
if (compare(list, right, order) <= 0) {
*tail = list;
list = list->peer;
} else {
*tail = right;
right = right->peer;
}
tail = &(*tail)->peer;
}
*tail = list != NODE_NULL ? list : right;

return head;
}

static void sorttree(struct node* np, int order)
{
struct node* subnp;
struct node *np0,
*np1,
*np2,
*np3;

/*
* sort the trees of each of this nodes children
Expand All @@ -504,31 +616,7 @@ static void sorttree(struct node* np, int order)
/*
* then put the given nodes children in order
*/
np0 = np; /* np0 points to node before np1 */
for (np1 = np->child; np1 != NODE_NULL; np1 = np1->peer) {
np2 = np1; /* np2 points to node before np3 */
for (np3 = np1->peer; np3 != NODE_NULL; np3 = np3->peer) {
if (compare(np3, np1, order) < 0) {
/*
* swap links
*/
if (np0 == np)
np0->child = np3;
else
np0->peer = np3;
np2->peer = np3->peer;
np3->peer = np1;

/*
* adjust pointers
*/
np1 = np3;
np3 = np2;
}
np2 = np3;
}
np0 = np1;
}
np->child = sortlist(np->child, order);
}

/*
Expand Down