summaryrefslogtreecommitdiff
path: root/tree.c
blob: 830a8865d11760a75fa9b2d48f8b7d5a009cb7b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include "tree.h"


struct tree_node* tree_make(const struct tree_pair *pairs, size_t npairs, size_t disk_size) {
  size_t datalen = 0;
  for (size_t i = 0; i < npairs; i++) {
    datalen += strlen(pairs[i].name) + 1 + sizeof(struct tree_node*);
  }
  datalen += 1;

  struct tree_node *const node = malloc(sizeof(struct tree_node));
  node->data = malloc(datalen);

  size_t total_files = 0;
  size_t total_dirs = 0;
  size_t total_size = disk_size;

  size_t cursor = 0;
  for (size_t i = 0; i < npairs; i++) {
    if (pairs[i].sub == NULL) {
      total_files++;
    } else {
      total_files += pairs[i].sub->total_files;
      total_dirs += 1 + pairs[i].sub->total_dirs;  // include this directory too
      total_size += pairs[i].sub->total_size;
    }

    uint8_t *dst = node->data + cursor;
    cursor += (uint8_t*)stpcpy((char*)dst, pairs[i].name) - dst;
    cursor++;
    *(struct tree_node**)(node->data + cursor) = pairs[i].sub;
    cursor += sizeof(struct tree_node*);
  }
  node->data[cursor] = 0;

  node->total_files = total_files;
  node->total_dirs = total_dirs;
  node->total_size = total_size;

  return node;
}

#define LOOP_NODE(node_, namevar_, subvar_, ...) do { \
    size_t _cursor = 0; \
    while (true) { \
      namevar_ = (char*)((node_)->data + _cursor); \
      size_t _len = strlen((char*)((node_)->data + _cursor)); \
      if (_len == 0) break; \
      _cursor += _len + 1; \
      subvar_ = *(struct tree_node**)((node_)->data + _cursor); \
      _cursor += sizeof(struct tree_node*); \
      { __VA_ARGS__ }; \
    } \
  } while (0)

void tree_free(struct tree_node *node) {
  char *name; (void)name;
  struct tree_node *sub;
  LOOP_NODE(node, name, sub, {
    if (sub != NULL) tree_free(sub);
  });
  free(node->data);
  free(node);
}

size_t tree_num_children(const struct tree_node *node) {
  char *name; (void)name;
  struct tree_node *sub; (void)sub;
  size_t count = 0;
  LOOP_NODE(node, name, sub, {
    count++;
  });
  return count;
}

struct tree_pair tree_index(struct tree_node *node, size_t index) {
  char *name;
  struct tree_node *sub;
  size_t count = 0;
  LOOP_NODE(node, name, sub, {
    if (count == index) return (struct tree_pair){.name = name, .sub = sub};
    count++;
  });
  assert(false);
}

struct tree_iter tree_iter_start(struct tree_node *node) {
  return (struct tree_iter){.node = node, .off = 0};
}

bool tree_iter_next(struct tree_iter *iter, struct tree_pair *dst) {
  struct tree_node *node = iter->node;
  if (node->data[iter->off] == 0) return false;

  dst->name = (char*)(node->data + iter->off);
  const size_t namelen = strlen(dst->name);
  dst->sub = *(struct tree_node**)(node->data + iter->off + namelen + 1);
  iter->off += namelen + 1 + sizeof(struct tree_node*);
  return true;
}