untracked cache: load from UNTR index extension
[gitweb.git] / dir.c
diff --git a/dir.c b/dir.c
index f39024c6396bf456a9ecbfa8dc886e83aebeb3d4..b39a034ae5c5fa28bdef7602207f93e9f3d6def6 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -12,6 +12,8 @@
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
+#include "varint.h"
+#include "ewah/ewok.h"
 
 struct path_simplify {
        int len;
@@ -2144,3 +2146,359 @@ void clear_directory(struct dir_struct *dir)
        }
        strbuf_release(&dir->basebuf);
 }
+
+struct ondisk_untracked_cache {
+       struct stat_data info_exclude_stat;
+       struct stat_data excludes_file_stat;
+       uint32_t dir_flags;
+       unsigned char info_exclude_sha1[20];
+       unsigned char excludes_file_sha1[20];
+       char exclude_per_dir[FLEX_ARRAY];
+};
+
+#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
+
+struct write_data {
+       int index;         /* number of written untracked_cache_dir */
+       struct ewah_bitmap *check_only; /* from untracked_cache_dir */
+       struct ewah_bitmap *valid;      /* from untracked_cache_dir */
+       struct ewah_bitmap *sha1_valid; /* set if exclude_sha1 is not null */
+       struct strbuf out;
+       struct strbuf sb_stat;
+       struct strbuf sb_sha1;
+};
+
+static void stat_data_to_disk(struct stat_data *to, const struct stat_data *from)
+{
+       to->sd_ctime.sec  = htonl(from->sd_ctime.sec);
+       to->sd_ctime.nsec = htonl(from->sd_ctime.nsec);
+       to->sd_mtime.sec  = htonl(from->sd_mtime.sec);
+       to->sd_mtime.nsec = htonl(from->sd_mtime.nsec);
+       to->sd_dev        = htonl(from->sd_dev);
+       to->sd_ino        = htonl(from->sd_ino);
+       to->sd_uid        = htonl(from->sd_uid);
+       to->sd_gid        = htonl(from->sd_gid);
+       to->sd_size       = htonl(from->sd_size);
+}
+
+static void write_one_dir(struct untracked_cache_dir *untracked,
+                         struct write_data *wd)
+{
+       struct stat_data stat_data;
+       struct strbuf *out = &wd->out;
+       unsigned char intbuf[16];
+       unsigned int intlen, value;
+       int i = wd->index++;
+
+       /*
+        * untracked_nr should be reset whenever valid is clear, but
+        * for safety..
+        */
+       if (!untracked->valid) {
+               untracked->untracked_nr = 0;
+               untracked->check_only = 0;
+       }
+
+       if (untracked->check_only)
+               ewah_set(wd->check_only, i);
+       if (untracked->valid) {
+               ewah_set(wd->valid, i);
+               stat_data_to_disk(&stat_data, &untracked->stat_data);
+               strbuf_add(&wd->sb_stat, &stat_data, sizeof(stat_data));
+       }
+       if (!is_null_sha1(untracked->exclude_sha1)) {
+               ewah_set(wd->sha1_valid, i);
+               strbuf_add(&wd->sb_sha1, untracked->exclude_sha1, 20);
+       }
+
+       intlen = encode_varint(untracked->untracked_nr, intbuf);
+       strbuf_add(out, intbuf, intlen);
+
+       /* skip non-recurse directories */
+       for (i = 0, value = 0; i < untracked->dirs_nr; i++)
+               if (untracked->dirs[i]->recurse)
+                       value++;
+       intlen = encode_varint(value, intbuf);
+       strbuf_add(out, intbuf, intlen);
+
+       strbuf_add(out, untracked->name, strlen(untracked->name) + 1);
+
+       for (i = 0; i < untracked->untracked_nr; i++)
+               strbuf_add(out, untracked->untracked[i],
+                          strlen(untracked->untracked[i]) + 1);
+
+       for (i = 0; i < untracked->dirs_nr; i++)
+               if (untracked->dirs[i]->recurse)
+                       write_one_dir(untracked->dirs[i], wd);
+}
+
+void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked)
+{
+       struct ondisk_untracked_cache *ouc;
+       struct write_data wd;
+       unsigned char varbuf[16];
+       int len = 0, varint_len;
+       if (untracked->exclude_per_dir)
+               len = strlen(untracked->exclude_per_dir);
+       ouc = xmalloc(sizeof(*ouc) + len + 1);
+       stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
+       stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
+       hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
+       hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
+       ouc->dir_flags = htonl(untracked->dir_flags);
+       memcpy(ouc->exclude_per_dir, untracked->exclude_per_dir, len + 1);
+       strbuf_add(out, ouc, ouc_size(len));
+       free(ouc);
+       ouc = NULL;
+
+       if (!untracked->root) {
+               varint_len = encode_varint(0, varbuf);
+               strbuf_add(out, varbuf, varint_len);
+               return;
+       }
+
+       wd.index      = 0;
+       wd.check_only = ewah_new();
+       wd.valid      = ewah_new();
+       wd.sha1_valid = ewah_new();
+       strbuf_init(&wd.out, 1024);
+       strbuf_init(&wd.sb_stat, 1024);
+       strbuf_init(&wd.sb_sha1, 1024);
+       write_one_dir(untracked->root, &wd);
+
+       varint_len = encode_varint(wd.index, varbuf);
+       strbuf_add(out, varbuf, varint_len);
+       strbuf_addbuf(out, &wd.out);
+       ewah_serialize_strbuf(wd.valid, out);
+       ewah_serialize_strbuf(wd.check_only, out);
+       ewah_serialize_strbuf(wd.sha1_valid, out);
+       strbuf_addbuf(out, &wd.sb_stat);
+       strbuf_addbuf(out, &wd.sb_sha1);
+       strbuf_addch(out, '\0'); /* safe guard for string lists */
+
+       ewah_free(wd.valid);
+       ewah_free(wd.check_only);
+       ewah_free(wd.sha1_valid);
+       strbuf_release(&wd.out);
+       strbuf_release(&wd.sb_stat);
+       strbuf_release(&wd.sb_sha1);
+}
+
+static void free_untracked(struct untracked_cache_dir *ucd)
+{
+       int i;
+       if (!ucd)
+               return;
+       for (i = 0; i < ucd->dirs_nr; i++)
+               free_untracked(ucd->dirs[i]);
+       for (i = 0; i < ucd->untracked_nr; i++)
+               free(ucd->untracked[i]);
+       free(ucd->untracked);
+       free(ucd->dirs);
+       free(ucd);
+}
+
+void free_untracked_cache(struct untracked_cache *uc)
+{
+       if (uc)
+               free_untracked(uc->root);
+       free(uc);
+}
+
+struct read_data {
+       int index;
+       struct untracked_cache_dir **ucd;
+       struct ewah_bitmap *check_only;
+       struct ewah_bitmap *valid;
+       struct ewah_bitmap *sha1_valid;
+       const unsigned char *data;
+       const unsigned char *end;
+};
+
+static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
+{
+       to->sd_ctime.sec  = get_be32(&from->sd_ctime.sec);
+       to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
+       to->sd_mtime.sec  = get_be32(&from->sd_mtime.sec);
+       to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
+       to->sd_dev        = get_be32(&from->sd_dev);
+       to->sd_ino        = get_be32(&from->sd_ino);
+       to->sd_uid        = get_be32(&from->sd_uid);
+       to->sd_gid        = get_be32(&from->sd_gid);
+       to->sd_size       = get_be32(&from->sd_size);
+}
+
+static int read_one_dir(struct untracked_cache_dir **untracked_,
+                       struct read_data *rd)
+{
+       struct untracked_cache_dir ud, *untracked;
+       const unsigned char *next, *data = rd->data, *end = rd->end;
+       unsigned int value;
+       int i, len;
+
+       memset(&ud, 0, sizeof(ud));
+
+       next = data;
+       value = decode_varint(&next);
+       if (next > end)
+               return -1;
+       ud.recurse         = 1;
+       ud.untracked_alloc = value;
+       ud.untracked_nr    = value;
+       if (ud.untracked_nr)
+               ud.untracked = xmalloc(sizeof(*ud.untracked) * ud.untracked_nr);
+       data = next;
+
+       next = data;
+       ud.dirs_alloc = ud.dirs_nr = decode_varint(&next);
+       if (next > end)
+               return -1;
+       ud.dirs = xmalloc(sizeof(*ud.dirs) * ud.dirs_nr);
+       data = next;
+
+       len = strlen((const char *)data);
+       next = data + len + 1;
+       if (next > rd->end)
+               return -1;
+       *untracked_ = untracked = xmalloc(sizeof(*untracked) + len);
+       memcpy(untracked, &ud, sizeof(ud));
+       memcpy(untracked->name, data, len + 1);
+       data = next;
+
+       for (i = 0; i < untracked->untracked_nr; i++) {
+               len = strlen((const char *)data);
+               next = data + len + 1;
+               if (next > rd->end)
+                       return -1;
+               untracked->untracked[i] = xstrdup((const char*)data);
+               data = next;
+       }
+
+       rd->ucd[rd->index++] = untracked;
+       rd->data = data;
+
+       for (i = 0; i < untracked->dirs_nr; i++) {
+               len = read_one_dir(untracked->dirs + i, rd);
+               if (len < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+static void set_check_only(size_t pos, void *cb)
+{
+       struct read_data *rd = cb;
+       struct untracked_cache_dir *ud = rd->ucd[pos];
+       ud->check_only = 1;
+}
+
+static void read_stat(size_t pos, void *cb)
+{
+       struct read_data *rd = cb;
+       struct untracked_cache_dir *ud = rd->ucd[pos];
+       if (rd->data + sizeof(struct stat_data) > rd->end) {
+               rd->data = rd->end + 1;
+               return;
+       }
+       stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
+       rd->data += sizeof(struct stat_data);
+       ud->valid = 1;
+}
+
+static void read_sha1(size_t pos, void *cb)
+{
+       struct read_data *rd = cb;
+       struct untracked_cache_dir *ud = rd->ucd[pos];
+       if (rd->data + 20 > rd->end) {
+               rd->data = rd->end + 1;
+               return;
+       }
+       hashcpy(ud->exclude_sha1, rd->data);
+       rd->data += 20;
+}
+
+static void load_sha1_stat(struct sha1_stat *sha1_stat,
+                          const struct stat_data *stat,
+                          const unsigned char *sha1)
+{
+       stat_data_from_disk(&sha1_stat->stat, stat);
+       hashcpy(sha1_stat->sha1, sha1);
+       sha1_stat->valid = 1;
+}
+
+struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
+{
+       const struct ondisk_untracked_cache *ouc;
+       struct untracked_cache *uc;
+       struct read_data rd;
+       const unsigned char *next = data, *end = (const unsigned char *)data + sz;
+       int len;
+
+       if (sz <= 1 || end[-1] != '\0')
+               return NULL;
+       end--;
+
+       ouc = (const struct ondisk_untracked_cache *)next;
+       if (next + ouc_size(0) > end)
+               return NULL;
+
+       uc = xcalloc(1, sizeof(*uc));
+       load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
+                      ouc->info_exclude_sha1);
+       load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
+                      ouc->excludes_file_sha1);
+       uc->dir_flags = get_be32(&ouc->dir_flags);
+       uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
+       /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
+       next += ouc_size(strlen(ouc->exclude_per_dir));
+       if (next >= end)
+               goto done2;
+
+       len = decode_varint(&next);
+       if (next > end || len == 0)
+               goto done2;
+
+       rd.valid      = ewah_new();
+       rd.check_only = ewah_new();
+       rd.sha1_valid = ewah_new();
+       rd.data       = next;
+       rd.end        = end;
+       rd.index      = 0;
+       rd.ucd        = xmalloc(sizeof(*rd.ucd) * len);
+
+       if (read_one_dir(&uc->root, &rd) || rd.index != len)
+               goto done;
+
+       next = rd.data;
+       len = ewah_read_mmap(rd.valid, next, end - next);
+       if (len < 0)
+               goto done;
+
+       next += len;
+       len = ewah_read_mmap(rd.check_only, next, end - next);
+       if (len < 0)
+               goto done;
+
+       next += len;
+       len = ewah_read_mmap(rd.sha1_valid, next, end - next);
+       if (len < 0)
+               goto done;
+
+       ewah_each_bit(rd.check_only, set_check_only, &rd);
+       rd.data = next + len;
+       ewah_each_bit(rd.valid, read_stat, &rd);
+       ewah_each_bit(rd.sha1_valid, read_sha1, &rd);
+       next = rd.data;
+
+done:
+       free(rd.ucd);
+       ewah_free(rd.valid);
+       ewah_free(rd.check_only);
+       ewah_free(rd.sha1_valid);
+done2:
+       if (next != end) {
+               free_untracked_cache(uc);
+               uc = NULL;
+       }
+       return uc;
+}