Add an io caching layer to the gitfo api The idea is taken from Junio's work in read-cache.c, where it's used for writing out the index without tap-dancing on the poor harddrive. Since it's almost certainly useful for cached writing of packfiles too, we turn it into a generic API, making it perfectly simple to reuse it later. gitfo_write_cached() has the same contract as gitfo_write(), it returns GIT_SUCCESS if all bytes are successfully written (or were at least buffered for later writing), and <0 if an error occurs during buffer writing. Signed-off-by: Andreas Ericsson <ae@op5.se> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
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 105 106 107 108 109 110 111 112 113 114 115 116 117
diff --git a/src/fileops.c b/src/fileops.c
index 1ca0fbb..62da145 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -47,3 +47,90 @@ off_t gitfo_size(git_file fd)
return -1;
return sb.st_size;
}
+
+/* cached diskio */
+struct gitfo_cache {
+ git_file fd;
+ unsigned int cache_size, pos;
+ void *cache;
+};
+
+gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size)
+{
+ gitfo_cache *ioc;
+
+ ioc = malloc(sizeof(*ioc));
+ if (!ioc)
+ return NULL;
+
+ ioc->pos = 0;
+ ioc->cache_size = cache_size;
+ ioc->cache = malloc(cache_size);
+ if (!ioc->cache) {
+ free(ioc);
+ return NULL;
+ }
+
+ return ioc;
+}
+
+static inline void gitfo_add_to_cache(gitfo_cache *ioc, void *buf, size_t len)
+{
+ memcpy(ioc->cache + ioc->pos, buf, len);
+ ioc->pos += len;
+}
+
+int gitfo_flush_cached(gitfo_cache *ioc)
+{
+ int result = GIT_SUCCESS;
+
+ if (ioc->pos) {
+ result = gitfo_write(ioc->fd, ioc->cache, ioc->pos);
+ ioc->pos = 0;
+ }
+
+ return result;
+}
+
+int gitfo_write_cached(gitfo_cache *ioc, void *buf, size_t len)
+{
+ for (;;) {
+ size_t space_left = ioc->cache_size - ioc->pos;
+ /* cache if it's small */
+ if (space_left > len) {
+ gitfo_add_to_cache(ioc, buf, len);
+ return GIT_SUCCESS;
+ }
+
+ /* flush the cache if it doesn't fit */
+ if (ioc->pos) {
+ int rc;
+ gitfo_add_to_cache(ioc, buf, space_left);
+ rc = gitfo_flush_cached(ioc);
+ if (rc < 0)
+ return rc;
+
+ len -= space_left;
+ buf += space_left;
+ }
+
+ /* write too-large chunks immediately */
+ if (len > ioc->cache_size)
+ return gitfo_write(ioc->fd, buf, len);
+ }
+ return GIT_SUCCESS;
+}
+
+int gitfo_close_cached(gitfo_cache *ioc)
+{
+ git_file fd;
+
+ if (gitfo_flush_cached(ioc) < 0)
+ return -1;
+
+ fd = ioc->fd;
+ free(ioc->cache);
+ free(ioc);
+
+ return gitfo_close(fd);
+}
diff --git a/src/fileops.h b/src/fileops.h
index 32d6b91..56d0888 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -21,6 +21,7 @@
typedef int git_file;
typedef struct stat gitfo_statbuf;
+typedef struct gitfo_cache gitfo_cache;
#define gitfo_open(path, flags) open(path, flags)
#define gitfo_close(fd) close(fd)
@@ -34,4 +35,9 @@ extern off_t gitfo_size(git_file fd);
#define gitfo_stat(path, buf) stat(path, buf)
#define gitfo_fsync(fd) fsync(fd)
+extern gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size);
+extern int gitfo_write_cached(gitfo_cache *ioc, void *buf, size_t len);
+extern int gitfo_flush_cached(gitfo_cache *ioc);
+extern int gitfo_close_cached(gitfo_cache *ioc);
+
#endif /* INCLUDE_fileops_h__ */