Commit 4188d28f1c38240392d896fc79561cc461fb12c0

Andreas Ericsson 2008-11-29T15:28:12

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>

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__ */