{"thread":{"id":"23884","subject":"[PATCH 0/7] Import David's SVN exporter","startedAt":"2010-05-23T21:40:25Z","lastAt":"2010-05-30T16:52:55Z","messageCount":29,"participants":["Ramkumar Ramachandra","Peter Baumann","David Michael Barr","Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"142160","messageId":"1274650832-7411-1-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":null,"subject":"[PATCH 0/7] Import David's SVN exporter","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:25Z","receivedAt":"2010-05-23T21:40:25Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Except for the first patch, this patch series attempts to include\nDavid's exporter [1] into git.git. I've made some efforts to go\nthrough every small detail in the code to clean and document it for\nclarity. If the code is more-or-less ready for inclusion (perhaps into\n'next'?), the plan is to continue developing the exporter on a\nseparate repository and pull changes into git.git. I haven't written a\nMakefile rule for building this yet.\n\nThe first patch includes the skeleton remote helper I sent last time\nwith some minor changes, and is still a WIP (I'm especially unhappy\nwith the Makefile rule): I'll post another patch shortly. I've only\nincluded it in this series to illustrate how I'll use David's\nexporter.\n\n[1]: http://github.com/artagnon/svn-dump-fast-export\n\n-- Ram\n\nRamkumar Ramachandra (7):\n  Add skeleton remote helper for SVN\n  Add cpp macro implementation of treaps\n  Add buffer pool library\n  Add a memory pool library\n  Add API for string-specific memory pool\n  Add SVN revision parser and exporter\n  Add handler for SVN dump\n\n Makefile              |    9 ++-\n remote-svn.c          |  201 +++++++++++++++++++++++++++++\n vcs-svn/fast_export.c |   61 +++++++++\n vcs-svn/fast_export.h |   17 +++\n vcs-svn/line_buffer.c |  115 +++++++++++++++++\n vcs-svn/line_buffer.h |   14 ++\n vcs-svn/obj_pool.h    |   61 +++++++++\n vcs-svn/repo_tree.c   |  333 +++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/repo_tree.h   |   31 +++++\n vcs-svn/string_pool.c |   84 +++++++++++++\n vcs-svn/string_pool.h |   11 ++\n vcs-svn/svnclient.c   |   20 +++\n vcs-svn/svnclient.h   |    7 +\n vcs-svn/svndump.c     |  277 ++++++++++++++++++++++++++++++++++++++++\n vcs-svn/svndump.h     |    7 +\n vcs-svn/trp.h         |  221 ++++++++++++++++++++++++++++++++\n 16 files changed, 1468 insertions(+), 1 deletions(-)\n create mode 100644 remote-svn.c\n create mode 100644 vcs-svn/fast_export.c\n create mode 100644 vcs-svn/fast_export.h\n create mode 100644 vcs-svn/line_buffer.c\n create mode 100644 vcs-svn/line_buffer.h\n create mode 100644 vcs-svn/obj_pool.h\n create mode 100644 vcs-svn/repo_tree.c\n create mode 100644 vcs-svn/repo_tree.h\n create mode 100644 vcs-svn/string_pool.c\n create mode 100644 vcs-svn/string_pool.h\n create mode 100644 vcs-svn/svnclient.c\n create mode 100644 vcs-svn/svnclient.h\n create mode 100644 vcs-svn/svndump.c\n create mode 100644 vcs-svn/svndump.h\n create mode 100644 vcs-svn/trp.h\n"},{"id":"142163","messageId":"1274650832-7411-2-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[WIP PATCH 1/7] Add skeleton remote helper for SVN","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:26Z","receivedAt":"2010-05-23T21:40:26Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Create remote-svn.c, which is essentially a stripped-down version of\nremote-curl.c to build the SVN remote helper upon. Also add a Makefile\nrule to build it.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Makefile            |    9 ++-\n remote-svn.c        |  201 +++++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/svnclient.c |   20 +++++\n vcs-svn/svnclient.h |    7 ++\n 4 files changed, 236 insertions(+), 1 deletions(-)\n create mode 100644 remote-svn.c\n create mode 100644 vcs-svn/svnclient.c\n create mode 100644 vcs-svn/svnclient.h\n\ndiff --git a/Makefile b/Makefile\nindex 4f7224a..91077a8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1658,7 +1658,7 @@ git.o git.spec \\\n \n TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))\n GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \\\n-\tgit.o http.o http-walker.o remote-curl.o\n+\tgit.o http.o http-walker.o remote-curl.o remote-svn.o\n XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \\\n \txdiff/xmerge.o xdiff/xpatience.o\n OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)\n@@ -1746,6 +1746,9 @@ endif\n %.s: %.c GIT-CFLAGS FORCE\n \t$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $<\n \n+remote-svn.o: remote-svn.c GIT-CFLAGS FORCE\n+\t$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) -I./vcs-svn $(EXTRA_CPPFLAGS) $<\n+\n ifdef USE_COMPUTED_HEADER_DEPENDENCIES\n # Take advantage of gcc's on-the-fly dependency generation\n # See <http://gcc.gnu.org/gcc-3.0/features.html>.\n@@ -1824,6 +1827,10 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n \t\t$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)\n \n+git-remote-svn$X: remote-svn.o $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@\t\\\n+\t\t$(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n+\n $(LIB_FILE): $(LIB_OBJS)\n \t$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)\n \ndiff --git a/remote-svn.c b/remote-svn.c\nnew file mode 100644\nindex 0000000..69d09c7\n--- /dev/null\n+++ b/remote-svn.c\n@@ -0,0 +1,201 @@\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+#include \"remote.h\"\n+#include \"strbuf.h\"\n+#include \"exec_cmd.h\"\n+\n+#include \"svnclient.h\"\n+\n+static struct remote *remote;\n+static const char *url;\n+\n+struct options {\n+\tint verbosity;\n+\tunsigned long depth;\n+\tunsigned progress : 1,\n+\t\tdry_run : 1;\n+};\n+static struct options options;\n+\n+static int set_option(const char *name, const char *value)\n+{\n+\tif (!strcmp(name, \"verbosity\")) {\n+\t\tchar *end;\n+\t\tint v = strtol(value, &end, 10);\n+\t\tif (value == end || *end)\n+\t\t\treturn -1;\n+\t\toptions.verbosity = v;\n+\t\treturn 0;\n+\t} else if (!strcmp(name, \"progress\")) {\n+\t\tif (!strcmp(value, \"true\"))\n+\t\t\toptions.progress = 1;\n+\t\telse if (!strcmp(value, \"false\"))\n+\t\t\toptions.progress = 0;\n+\t\telse\n+\t\t\treturn -1;\n+\t\treturn 0;\n+\t} else if (!strcmp(name, \"dry-run\")) {\n+\t\tif (!strcmp(value, \"true\"))\n+\t\t\toptions.dry_run = 1;\n+\t\telse if (!strcmp(value, \"false\"))\n+\t\t\toptions.dry_run = 0;\n+\t\telse\n+\t\t\treturn -1;\n+\t\treturn 0;\n+\t} else {\n+\t\treturn 1;\n+\t}\n+}\n+\n+static int export_handler(int nr_spec, char **specs)\n+{\n+\tint err = 0;\n+\n+\t/* TODO: The real exporting */\n+        /* TODO: Write an importer for SVN */\n+\t\n+\treturn err;\n+}\n+\n+static int import_handler(int nr_spec, char **specs)\n+{\n+\tint err = 0;\n+\t\n+\t/* TODO: The real importing */\n+\t/* TODO: Hook up an SVN exporter's fast-export stream */\n+\n+\treturn err;\n+}\n+\n+static void parse_import(struct strbuf *buf)\n+{\n+\tchar **specs = NULL;\n+\tint alloc_spec = 0, nr_spec = 0, i;\n+\n+\tdo {\n+\t\tif (!prefixcmp(buf->buf, \"import \")) {\n+\t\t\tALLOC_GROW(specs, nr_spec + 1, alloc_spec);\n+\t\t\tspecs[nr_spec++] = xstrdup(buf->buf + 5);\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"remote helper does not support %s\", buf->buf);\n+\n+\t\tstrbuf_reset(buf);\n+\t\tif (strbuf_getline(buf, stdin, '\\n') == EOF)\n+\t\t\treturn;\n+\t\tif (!*buf->buf)\n+\t\t\tbreak;\n+\t} while (1);\n+\n+\tif (import_handler(nr_spec, specs))\n+\t\texit(128); /* error already reported */\n+\tfor (i = 0; i < nr_spec; i++)\n+\t\tfree(specs[i]);\n+\tfree(specs);\n+\n+\tprintf(\"\\n\");\n+\tfflush(stdout);\n+}\n+\n+static void parse_export(struct strbuf *buf)\n+{\n+\tchar **specs = NULL;\n+\tint alloc_spec = 0, nr_spec = 0, i;\n+\n+\tdo {\n+\t\tif (!prefixcmp(buf->buf, \"export \")) {\n+\t\t\tALLOC_GROW(specs, nr_spec + 1, alloc_spec);\n+\t\t\tspecs[nr_spec++] = xstrdup(buf->buf + 5);\n+\t\t} else\n+\t\t\tdie(\"remote helper does not support %s\", buf->buf);\n+\n+\t\tstrbuf_reset(buf);\n+\t\tif (strbuf_getline(buf, stdin, '\\n') == EOF)\n+\t\t\treturn;\n+\t\tif (!*buf->buf)\n+\t\t\tbreak;\n+\t} while (1);\n+\n+\tif (export_handler(nr_spec, specs))\n+\t\texit(128); /* error already reported */\n+\tfor (i = 0; i < nr_spec; i++)\n+\t\tfree(specs[i]);\n+\tfree(specs);\n+\n+\tprintf(\"\\n\");\n+\tfflush(stdout);\n+}\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint nongit;\n+\n+\tgit_extract_argv0_path(argv[0]);\n+\tsetup_git_directory_gently(&nongit);\n+\tif (argc < 2) {\n+\t\tfprintf(stderr, \"Remote needed\\n\");\n+\t\treturn 1;\n+\t}\n+\n+\toptions.verbosity = 1;\n+\toptions.progress = !!isatty(2);\n+\n+\tremote = remote_get(argv[1]);\n+\n+\tif (argc > 2) {\n+\t\turl = argv[2];\n+\t} else {\n+\t\turl = remote->url[0];\n+\t}\n+\n+\topen_svn_connection(remote);\n+\tdo {\n+\t\tif (strbuf_getline(&buf, stdin, '\\n') == EOF)\n+\t\t\tbreak;\n+\n+\t\telse if (!strcmp(buf.buf, \"list\")) {\n+\t\t\t/* TODO: Code to list refs */\n+\t\t\tbreak;\n+\n+\t\t} else if (!prefixcmp(buf.buf, \"import \")) {\n+\t\t\tif (nongit)\n+\t\t\t\tdie(\"Import attempted without a local repo\");\n+\n+\t\t\tparse_import(&buf);\n+\n+\t\t} else if (!prefixcmp(buf.buf, \"export \")) {\n+\t\t\tparse_export(&buf);\n+\n+\t\t} else if (!prefixcmp(buf.buf, \"option \")) {\n+\t\t\tchar *name = buf.buf + strlen(\"option \");\n+\t\t\tchar *value = strchr(name, ' ');\n+\t\t\tint result;\n+\t\t\tif (value)\n+\t\t\t\t*value++ = '\\0';\n+\t\t\telse\n+\t\t\t\tvalue = \"true\";\n+\t\t\tresult = set_option(name, value);\n+\t\t\tif (!result)\n+\t\t\t\tprintf(\"ok\\n\");\n+\t\t\telse if (result < 0)\n+\t\t\t\tprintf(\"error invalid value\\n\");\n+\t\t\telse\n+\t\t\t\tprintf(\"unsupported\\n\");\n+\t\t\tfflush(stdout);\n+\n+\t\t} else if (!strcmp(buf.buf, \"capabilities\")) {\n+\t\t\tprintf(\"option\\n\");\n+\t\t\tprintf(\"import\\n\");\n+\t\t\tprintf(\"export\\n\");\n+\t\t\tprintf(\"\\n\");\n+\t\t\tfflush(stdout);\n+\t\t} else\n+\t\t\treturn 1;\n+\n+\t\tstrbuf_reset(&buf);\n+\t} while (1);\n+\n+\tclose_svn_connection(remote);\n+\treturn 0;\n+}\ndiff --git a/vcs-svn/svnclient.c b/vcs-svn/svnclient.c\nnew file mode 100644\nindex 0000000..2a3ca44\n--- /dev/null\n+++ b/vcs-svn/svnclient.c\n@@ -0,0 +1,20 @@\n+int open_svn_connection(struct remote *remote)\n+{\n+        /* TODO: Open connection to remote */\n+}\n+\n+int close_svn_connection(struct remote *remote)\n+{\n+        /* TODO: Close connection */\n+}\n+\n+int FI_svn (unsigned char *sha1, const char *ref)\n+{\n+        /* TODO: Hook up fast-export stream from SVN exporter */\n+}\n+\n+int FE_svn (unsigned char *sha1, const char *ref)\n+{\n+        /* TODO: Write a fast-import module for SVN to import from the\n+           Git fast-export stream */\n+}\ndiff --git a/vcs-svn/svnclient.h b/vcs-svn/svnclient.h\nnew file mode 100644\nindex 0000000..ecd5e8b\n--- /dev/null\n+++ b/vcs-svn/svnclient.h\n@@ -0,0 +1,7 @@\n+#ifndef SVNCLIENT_H\n+#define SVNCLIENT_H\n+\n+int open_svn_connection(struct remote *remote);\n+int close_svn_connection(struct remote *remote);\n+\n+#endif\n-- \n1.7.1\n"},{"id":"142165","messageId":"1274650832-7411-3-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/7] Add cpp macro implementation of treaps","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:27Z","receivedAt":"2010-05-23T21:40:27Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"The implementation exposes an API to generate type-specific treap\nimplmentation and various functions to operate on it. Taken directly\nfrom David Michael Barr's svn-dump-fast-export repository.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/trp.h |  221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 221 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/trp.h\n\ndiff --git a/vcs-svn/trp.h b/vcs-svn/trp.h\nnew file mode 100644\nindex 0000000..d40f9d3\n--- /dev/null\n+++ b/vcs-svn/trp.h\n@@ -0,0 +1,221 @@\n+/******************************************************************************\n+ *\n+ * cpp macro implementation of treaps.\n+ *\n+ * Usage:\n+ *\n+ *   #include <trp.h>\n+ *   trp(...)\n+ *   trp_gen(...)\n+ *   ...\n+ *\n+ ******************************************************************************/\n+\n+#ifndef TRP_H_\n+#define\tTRP_H_\n+\n+#include <stdint.h>\n+\n+/* Node structure. */\n+#define\ttrp_node(a_type)\t\t\t\t\t\t\\\n+struct {\t\t\t\t\t\t\t\t\\\n+    uint32_t trpn_left;\t\t\t\t\t\t\t\\\n+    uint32_t trpn_right;\t\t\t\t\t\t\\\n+}\n+\n+/* Root structure. */\n+#define\ttrp(a_type)\t\t\t\t\t\t\t\\\n+struct {\t\t\t\t\t\t\t\t\\\n+    uint32_t trp_root;\t\t\t\t\t\t\t\\\n+}\n+\n+/* Pointer/Offset conversion */\n+#define trpn_pointer(a_base, a_offset)\t\t\t\t\t\\\n+    (a_base##_pointer(a_offset))\n+#define trpn_offset(a_base, a_pointer)\t\t\t\t        \\\n+    (a_base##_offset(a_pointer))\n+\n+/* Left accessors. */\n+#define\ttrp_left_get(a_base, a_type, a_field, a_node)\t\t\t\\\n+    trpn_pointer(a_base, (a_node)->a_field.trpn_left)\n+#define\ttrp_left_set(a_base, a_type, a_field, a_node, a_left) do {\t\\\n+    (a_node)->a_field.trpn_left = trpn_offset(a_base, a_left);\t        \\\n+} while (0)\n+\n+/* Right accessors. */\n+#define\ttrp_right_get(a_base, a_type, a_field, a_node)\t\t\t\\\n+    trpn_pointer(a_base, (a_node)->a_field.trpn_right)\n+#define\ttrp_right_set(a_base, a_type, a_field, a_node, a_right) do {\t\\\n+    (a_node)->a_field.trpn_right = trpn_offset(a_base, a_right);        \\\n+} while (0)\n+\n+/* Priority accessors. */\n+#define\ttrp_prio_get(a_type, a_field, a_node)\t\t\t\t\\\n+    (2654435761*(uint32_t)(uintptr_t)(a_node))\n+\n+/* Node initializer. */\n+#define\ttrp_node_new(a_base, a_type, a_field, a_trp, a_node) do {\t\\\n+    trp_left_set(a_base, a_type, a_field, (a_node), NULL);\t\t\\\n+    trp_right_set(a_base, a_type, a_field, (a_node), NULL);\t\t\\\n+} while (0)\n+\n+/* Tree initializer. */\n+#define\ttrp_new(a_type, a_base, a_trp) do {\t\t        \t\\\n+    (a_trp)->trp_root = trpn_offset(a_base, NULL);\t\t\t\\\n+} while (0)\n+\n+/* Internal utility macros. */\n+#define\ttrpn_rotate_left(a_base, a_type, a_field, a_node, r_node) do {\t\\\n+    (r_node) = trp_right_get(a_base, a_type, a_field, (a_node));\t\\\n+    trp_right_set(a_base, a_type, a_field, (a_node),\t\t\t\\\n+      trp_left_get(a_base, a_type, a_field, (r_node)));\t\t\t\\\n+    trp_left_set(a_base, a_type, a_field, (r_node), (a_node));\t\t\\\n+} while (0)\n+\n+#define\ttrpn_rotate_right(a_base, a_type, a_field, a_node, r_node) do {\t\\\n+    (r_node) = trp_left_get(a_base, a_type, a_field, (a_node));\t\t\\\n+    trp_left_set(a_base, a_type, a_field, (a_node),\t\t\t\\\n+      trp_right_get(a_base, a_type, a_field, (r_node)));\t\t\\\n+    trp_right_set(a_base, a_type, a_field, (r_node), (a_node));\t\t\\\n+} while (0)\n+\n+/*\n+ * The trp_gen() macro generates a type-specific treap implementation,\n+ * based on the above cpp macros.\n+ *\n+ * Arguments:\n+ *\n+ *   a_attr     : Function attribute for generated functions (ex: static).\n+ *   a_pre      : Prefix for generated functions (ex: treap_).\n+ *   a_t_type   : Type for treap data structure (ex: treap_t).\n+ *   a_type     : Type for treap node data structure (ex: treap_node_t).\n+ *   a_field    : Name of treap node linkage (ex: treap_link).\n+ *   a_base     : Expression for the base pointer from which nodes are offset.\n+ *   a_cmp      : Node comparison function name, with the following prototype:\n+ *                  int (a_cmp *)(a_type *a_node, a_type *a_other);\n+ *                                        ^^^^^^\n+ *                                     or a_key\n+ *                Interpretation of comparision function return values:\n+ *                  -1 : a_node <  a_other\n+ *                   0 : a_node == a_other\n+ *                   1 : a_node >  a_other\n+ *                In all cases, the a_node or a_key macro argument is the first\n+ *                argument to the comparison function, which makes it possible\n+ *                to write comparison functions that treat the first argument\n+ *                specially.\n+ *\n+ * Assuming the following setup:\n+ *\n+ *   typedef struct ex_node_s ex_node_t;\n+ *   struct ex_node_s {\n+ *       trp_node(ex_node_t) ex_link;\n+ *   };\n+ *   typedef trp(ex_node_t) ex_t;\n+ *   static ex_node_t ex_base[MAX_NODES];\n+ *   trp_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_base, ex_cmp)\n+ *\n+ * The following API is generated:\n+ *\n+ *   static void\n+ *   ex_new(ex_t *treap);\n+ *       Description: Initialize a treap structure.\n+ *       Args:\n+ *         treap: Pointer to an uninitialized treap object.\n+ *\n+ *   static ex_node_t *\n+ *   ex_psearch(ex_t *treap, ex_node_t *key);\n+ *       Description: Search for node that matches key.  If no match is found,\n+ *                    return what would be key's successor/predecessor, were\n+ *                    key in treap.\n+ *       Args:\n+ *         treap: Pointer to a initialized treap object.\n+ *         key  : Search key.\n+ *       Ret: Node in treap that matches key, or if no match, hypothetical\n+ *            node's successor/predecessor (NULL if no successor/predecessor).\n+ *\n+ *   static void\n+ *   ex_insert(ex_t *treap, ex_node_t *node);\n+ *       Description: Insert node into treap.\n+ *       Args:\n+ *         treap: Pointer to a initialized treap object.\n+ *         node : Node to be inserted into treap.\n+ */\n+#define\ttrp_gen(a_attr, a_pre, a_t_type, a_type, a_field, a_base, a_cmp)\\\n+a_attr void\t\t\t\t\t\t\t\t\\\n+a_pre##new(a_t_type *treap) {           \t\t\t\t\\\n+    trp_new(a_type, a_base, treap);\t\t\t        \t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+a_attr a_type *\t\t\t\t\t\t\t\t\\\n+a_pre##search(a_t_type *treap, a_type *key) {\t\t\t\t\\\n+    a_type *ret;\t\t\t\t\t\t\t\\\n+    int cmp;\t\t\t\t\t\t\t\t\\\n+    ret = trpn_pointer(a_base, treap->trp_root);\t\t\t\\\n+    while (ret != NULL\t\t\t\t\t\t\t\\\n+      && (cmp = (a_cmp)(key, ret)) != 0) {\t\t\t\t\\\n+\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n+\t    ret = trp_left_get(a_base, a_type, a_field, ret);\t\t\\\n+\t} else {\t\t\t\t\t\t\t\\\n+\t    ret = trp_right_get(a_base, a_type, a_field, ret);\t\t\\\n+\t}\t\t\t\t\t\t\t\t\\\n+    }\t\t\t\t\t\t\t\t\t\\\n+    return (ret);\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+a_attr a_type *\t\t\t\t\t\t\t\t\\\n+a_pre##psearch(a_t_type *treap, a_type *key) {\t\t\t\t\\\n+    a_type *ret;\t\t\t\t\t\t\t\\\n+    a_type *tnode = trpn_pointer(a_base, treap->trp_root);\t\t\\\n+    ret = NULL;\t\t\t\t\t\t\t\t\\\n+    while (tnode != NULL) {\t\t\t\t\t\t\\\n+\tint cmp = (a_cmp)(key, tnode);\t\t\t\t\t\\\n+\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n+\t    tnode = trp_left_get(a_base, a_type, a_field, tnode);\t\\\n+\t} else if (cmp > 0) {\t\t\t\t\t\t\\\n+\t    ret = tnode;\t\t\t\t\t\t\\\n+\t    tnode = trp_right_get(a_base, a_type, a_field, tnode);\t\\\n+\t} else {\t\t\t\t\t\t\t\\\n+\t    ret = tnode;\t\t\t\t\t\t\\\n+\t    break;\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\\\n+    }\t\t\t\t\t\t\t\t\t\\\n+    return (ret);\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+a_attr a_type *\t\t\t\t\t\t\t\t\\\n+a_pre##insert_recurse(a_type *cur_node, a_type *ins_node) {\t\t\\\n+    if (cur_node == NULL) {\t\t\t\t\t\t\\\n+\treturn (ins_node);\t\t\t\t\t\t\\\n+    } else {\t\t\t\t\t\t\t\t\\\n+\ta_type *ret;\t\t\t\t\t\t\t\\\n+\tint cmp = a_cmp(ins_node, cur_node);\t\t\t\t\\\n+\tif (cmp < 0) {\t\t\t\t\t\t\t\\\n+\t    a_type *left = a_pre##insert_recurse(trp_left_get(a_base,\t\\\n+              a_type, a_field, cur_node), ins_node);\t\t\t\\\n+\t    trp_left_set(a_base, a_type, a_field, cur_node, left);\t\\\n+\t    if (trp_prio_get(a_type, a_field, left) <\t\t\t\\\n+\t      trp_prio_get(a_type, a_field, cur_node)) {\t\t\\\n+\t\ttrpn_rotate_right(a_base, a_type, a_field, cur_node,\t\\\n+                  ret);\t\t\t\t\t\t\t\\\n+\t    } else {\t\t\t\t\t\t\t\\\n+\t\tret = cur_node;\t\t\t\t\t\t\\\n+\t    }\t\t\t\t\t\t\t\t\\\n+\t} else {\t\t\t\t\t\t\t\\\n+\t    a_type *right = a_pre##insert_recurse(trp_right_get(a_base, \\\n+\t      a_type, a_field, cur_node), ins_node);\t\t\t\\\n+\t    trp_right_set(a_base, a_type, a_field, cur_node, right);\t\\\n+\t    if (trp_prio_get(a_type, a_field, right) <\t\t\t\\\n+\t      trp_prio_get(a_type, a_field, cur_node)) {\t\t\\\n+\t\ttrpn_rotate_left(a_base, a_type, a_field, cur_node,\t\\\n+                  ret);\t\t\t\t\t\t\t\\\n+\t    } else {\t\t\t\t\t\t\t\\\n+\t\tret = cur_node;\t\t\t\t\t\t\\\n+\t    }\t\t\t\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\t\\\n+\treturn (ret);\t\t\t\t\t\t\t\\\n+    }\t\t\t\t\t\t\t\t\t\\\n+}\t\t\t\t\t\t\t\t\t\\\n+a_attr void\t\t\t\t\t\t\t\t\\\n+a_pre##insert(a_t_type *treap, a_type *node) {\t\t\t\t\\\n+    trp_node_new(a_base, a_type, a_field, treap, node);\t\t\t\\\n+    treap->trp_root = trpn_offset(a_base, a_pre##insert_recurse(        \\\n+        trpn_pointer(a_base, treap->trp_root), node));\t                \\\n+}\n+#endif                          /* TRP_H_ */\n-- \n1.7.1\n"},{"id":"142167","messageId":"1274650832-7411-4-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 3/7] Add buffer pool library","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:28Z","receivedAt":"2010-05-23T21:40:28Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"line_buffer creates a couple of static buffers and expose an API for\nusing them. The idea is to maintain a fixed static memory pool to\navoid constant allocation and de-allocation of memory. Taken directly\nfrom David Michael Barr's svn-dump-fast-export repository.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/line_buffer.c |  115 +++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/line_buffer.h |   14 ++++++\n 2 files changed, 129 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/line_buffer.c\n create mode 100644 vcs-svn/line_buffer.h\n\ndiff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c\nnew file mode 100644\nindex 0000000..6b47d38\n--- /dev/null\n+++ b/vcs-svn/line_buffer.c\n@@ -0,0 +1,115 @@\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include \"line_buffer.h\"\n+\n+#define LINE_BUFFER_LEN 10000\n+#define COPY_BUFFER_LEN 4096\n+\n+static char line_buffer[LINE_BUFFER_LEN];\n+static char byte_buffer[COPY_BUFFER_LEN];\n+static uint32_t line_buffer_len = 0;\n+static uint32_t line_len = 0;\n+\n+char *buffer_read_line(void)\n+{\n+    char *end;\n+    uint32_t n_read;\n+\n+    if (line_len) {\n+        memmove(line_buffer, &line_buffer[line_len],\n+                line_buffer_len - line_len);\n+        line_buffer_len -= line_len;\n+        line_len = 0;\n+    }\n+\n+    end = memchr(line_buffer, '\\n', line_buffer_len);\n+    while (line_buffer_len < LINE_BUFFER_LEN - 1 &&\n+           !feof(stdin) && NULL == end) {\n+        n_read =\n+            fread(&line_buffer[line_buffer_len], 1,\n+                  LINE_BUFFER_LEN - 1 - line_buffer_len,\n+                  stdin);\n+        end = memchr(&line_buffer[line_buffer_len], '\\n', n_read);\n+        line_buffer_len += n_read;\n+    }\n+\n+    if (ferror(stdin))\n+        return NULL;\n+\n+    if (end != NULL) {\n+        line_len = end - line_buffer;\n+        line_buffer[line_len++] = '\\0';\n+    } else {\n+        line_len = line_buffer_len;\n+        line_buffer[line_buffer_len] = '\\0';\n+    }\n+\n+    if (line_len == 0)\n+        return NULL;\n+\n+    return line_buffer;\n+}\n+\n+char *buffer_read_string(uint32_t len)\n+{\n+    char *s = malloc(len + 1);\n+    uint32_t offset = 0;\n+    if (line_buffer_len > line_len) {\n+        offset = line_buffer_len - line_len;\n+        if (offset > len)\n+            offset = len;\n+        memcpy(s, &line_buffer[line_len], offset);\n+        line_len += offset;\n+    }\n+    while (offset < len && !feof(stdin)) {\n+        offset += fread(&s[offset], 1, len - offset, stdin);\n+    }\n+    s[offset] = '\\0';\n+    return s;\n+}\n+\n+void buffer_copy_bytes(uint32_t len)\n+{\n+    uint32_t in, out;\n+    if (line_buffer_len > line_len) {\n+        in = line_buffer_len - line_len;\n+        if (in > len)\n+            in = len;\n+        out = 0;\n+        while (out < in && !ferror(stdout)) {\n+            out +=\n+                fwrite(&line_buffer[line_len + out], 1, in - out, stdout);\n+        }\n+        len -= in;\n+        line_len += in;\n+    }\n+    while (len > 0 && !feof(stdin)) {\n+        in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;\n+        in = fread(byte_buffer, 1, in, stdin);\n+        len -= in;\n+        out = 0;\n+        while (out < in && !ferror(stdout)) {\n+            out += fwrite(&byte_buffer[out], 1, in - out, stdout);\n+        }\n+    }\n+}\n+\n+void buffer_skip_bytes(uint32_t len)\n+{\n+    uint32_t in;\n+    if (line_buffer_len > line_len) {\n+        in = line_buffer_len - line_len;\n+        if (in > len)\n+            in = len;\n+        line_len += in;\n+        len -= in;\n+    }\n+    while (len > 0 && !feof(stdin)) {\n+        in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;\n+        in = fread(byte_buffer, 1, in, stdin);\n+        len -= in;\n+    }\n+}\n+\ndiff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h\nnew file mode 100644\nindex 0000000..e546f4d\n--- /dev/null\n+++ b/vcs-svn/line_buffer.h\n@@ -0,0 +1,14 @@\n+#ifndef LINE_BUFFER_H_\n+#define LINE_BUFFER_H_\n+\n+#include <stdint.h>\n+\n+char *buffer_read_line(void);\n+\n+char *buffer_read_string(uint32_t len);\n+\n+void buffer_copy_bytes(uint32_t len);\n+\n+void buffer_skip_bytes(uint32_t len);\n+\n+#endif\n-- \n1.7.1\n"},{"id":"142162","messageId":"1274650832-7411-5-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 4/7] Add a memory pool library","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:29Z","receivedAt":"2010-05-23T21:40:29Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Add a memory pool library implemented using cpp macros. The macro can\nbe used to create a type-specific memory pool API. The memory nodes\nthemselves are stored in a treap, using macros in trp.h. Taken\ndirectly from David Michael Barr's svn-dump-fast-export repository.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/obj_pool.h |   61 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 61 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/obj_pool.h\n\ndiff --git a/vcs-svn/obj_pool.h b/vcs-svn/obj_pool.h\nnew file mode 100644\nindex 0000000..d8b0842\n--- /dev/null\n+++ b/vcs-svn/obj_pool.h\n@@ -0,0 +1,61 @@\n+#ifndef OBJ_POOL_H_\n+#define OBJ_POOL_H_\n+\n+#include <stdint.h>\n+#include <stdlib.h>\n+\n+/*\n+ * The obj_pool_gen() macro generates a type-specific memory pool\n+ * implementation.\n+ *\n+ * Arguments:\n+ *\n+ *   pre              : Prefix for generated functions (ex: string_).\n+ *   obj_t            : Type for treap data structure (ex: char).\n+ *   intial_capacity  : The initial size of the memory pool (ex: 4096).\n+ *\n+ */\n+#define obj_pool_gen(pre, obj_t, initial_capacity)                         \\\n+static struct {                                                            \\\n+    uint32_t size;                                                         \\\n+    uint32_t capacity;                                                     \\\n+    obj_t *base;                                                           \\\n+} pre##_pool = { 0, 0, NULL};                                              \\\n+static uint32_t pre##_alloc(uint32_t count)                                \\\n+{                                                                          \\\n+    uint32_t offset;                                                       \\\n+    while (pre##_pool.size + count > pre##_pool.capacity) {                \\\n+        if (pre##_pool.capacity) {                                         \\\n+            pre##_pool.capacity *= 2;                                      \\\n+        } else {                                                           \\\n+            pre##_pool.capacity = initial_capacity;                        \\\n+        }                                                                  \\\n+        pre##_pool.base =                                                  \\\n+            realloc(pre##_pool.base, pre##_pool.capacity * sizeof(obj_t)); \\\n+    }                                                                      \\\n+    offset = pre##_pool.size;                                              \\\n+    pre##_pool.size += count;                                              \\\n+    return offset;                                                         \\\n+}                                                                          \\\n+static void pre##_free(uint32_t count)                                     \\\n+{                                                                          \\\n+    pre##_pool.size -= count;                                              \\\n+}                                                                          \\\n+static uint32_t pre##_offset(obj_t *obj)                                   \\\n+{                                                                          \\\n+    return obj == NULL ? ~0 : obj - pre##_pool.base;                       \\\n+}                                                                          \\\n+static obj_t *pre##_pointer(uint32_t offset)                               \\\n+{                                                                          \\\n+    return offset >= pre##_pool.size ? NULL : &pre##_pool.base[offset];    \\\n+}                                                                          \\\n+static void pre##_reset(void)                                              \\\n+{                                                                          \\\n+    if (pre##_pool.base)                                                   \\\n+        free(pre##_pool.base);                                             \\\n+    pre##_pool.base = NULL;                                                \\\n+    pre##_pool.size = 0;                                                   \\\n+    pre##_pool.capacity = 0;                                               \\\n+}                                                                          \\\n+\n+#endif\n-- \n1.7.1\n"},{"id":"142161","messageId":"1274650832-7411-6-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 5/7] Add API for string-specific memory pool","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:30Z","receivedAt":"2010-05-23T21:40:30Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"string_pool uses the macros in the memory pool library to create a\nmemory pool for strings and expose an API for handling the\nstrings. Taken directly from David Michael Barr's svn-dump-fast-export\nrepository.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/string_pool.c |   84 +++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/string_pool.h |   11 ++++++\n 2 files changed, 95 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/string_pool.c\n create mode 100644 vcs-svn/string_pool.h\n\ndiff --git a/vcs-svn/string_pool.c b/vcs-svn/string_pool.c\nnew file mode 100644\nindex 0000000..4624134\n--- /dev/null\n+++ b/vcs-svn/string_pool.c\n@@ -0,0 +1,84 @@\n+#include <string.h>\n+\n+#include \"trp.h\"\n+#include \"obj_pool.h\"\n+#include \"string_pool.h\"\n+\n+typedef struct node_s node_t;\n+typedef trp(node_t) tree_t;\n+static tree_t tree = { ~0 };\n+\n+struct node_s {\n+    uint32_t offset;\n+    trp_node(node_t) children;\n+};\n+\n+/* Create two memory pools: one for node_t, and another for strings */\n+obj_pool_gen(node, node_t, 4096);\n+obj_pool_gen(string, char, 4096);\n+\n+static char *node_value(node_t *node)\n+{\n+    return node ? string_pointer(node->offset) : NULL;\n+}\n+\n+static int node_value_cmp(node_t *a, node_t *b)\n+{\n+    return strcmp(node_value(a), node_value(b));\n+}\n+\n+static int node_indentity_cmp(node_t *a, node_t *b)\n+{\n+    int r = node_value_cmp(a, b);\n+    return r ? r : (((uintptr_t) a) > ((uintptr_t) b))\n+        - (((uintptr_t) a) < ((uintptr_t) b));\n+}\n+\n+/* Build a Treap from the node_s structure (a trp_node w/ offset) */\n+trp_gen(static, tree_, tree_t, node_t, children, node,\n+        node_indentity_cmp);\n+\n+static char *pool_fetch(uint32_t entry)\n+{\n+    return node_value(node_pointer(entry));\n+}\n+\n+uint32_t pool_intern(char *key)\n+{\n+    node_t *match = NULL;\n+    uint32_t key_len = strlen(key) + 1;\n+    node_t *node = node_pointer(node_alloc(1));\n+    node->offset = string_alloc(key_len);\n+    strcpy(node_value(node), key);\n+    match = tree_psearch(&tree, node);\n+    if (!match || node_value_cmp(node, match)) {\n+        tree_insert(&tree, node);\n+    } else {\n+        node_free(1);\n+        string_free(key_len);\n+        node = match;\n+    }\n+    return node_offset(node);\n+}\n+\n+uint32_t pool_tok_r(char *str, const char *delim, char **saveptr)\n+{\n+    char *token = strtok_r(str, delim, saveptr);\n+    return token ? pool_intern(token) : ~0;\n+}\n+\n+void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream)\n+{\n+    uint32_t i;\n+    for (i = 0; i < len; i++) {\n+        fputs(pool_fetch(seq[i]), stream);\n+        if (i < len - 1)\n+            fputc(delim, stream);\n+    }\n+}\n+\n+void pool_reset(void)\n+{\n+    node_reset();\n+    string_reset();\n+}\ndiff --git a/vcs-svn/string_pool.h b/vcs-svn/string_pool.h\nnew file mode 100644\nindex 0000000..fb9e6b8\n--- /dev/null\n+++ b/vcs-svn/string_pool.h\n@@ -0,0 +1,11 @@\n+#ifndef STRING_POOL_H_\n+#define\tSTRING_POOL_H_\n+\n+#include <stdint.h>\n+#include <stdio.h>\n+\n+uint32_t pool_tok_r(char *str, const char *delim, char **saveptr);\n+void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream);\n+void pool_reset(void);\n+\n+#endif\n-- \n1.7.1\n"},{"id":"142164","messageId":"1274650832-7411-7-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 6/7] Add SVN revision parser and exporter","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:31Z","receivedAt":"2010-05-23T21:40:31Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"repo_tree parses SVN revisions to build a Git objects, and use\nfast_export to emit them so they can be imported into the Git object\nstore via a fast-import. Taken directly from David Michael Barr's\nsvn-dump-fast-export repository.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/fast_export.c |   61 +++++++++\n vcs-svn/fast_export.h |   17 +++\n vcs-svn/repo_tree.c   |  333 +++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/repo_tree.h   |   31 +++++\n 4 files changed, 442 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/fast_export.c\n create mode 100644 vcs-svn/fast_export.h\n create mode 100644 vcs-svn/repo_tree.c\n create mode 100644 vcs-svn/repo_tree.h\n\ndiff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c\nnew file mode 100644\nindex 0000000..f4d9ab7\n--- /dev/null\n+++ b/vcs-svn/fast_export.c\n@@ -0,0 +1,61 @@\n+#include <string.h>\n+\n+#include \"fast_export.h\"\n+#include \"line_buffer.h\"\n+#include \"repo_tree.h\"\n+#include \"string_pool.h\"\n+\n+#define MAX_GITSVN_LINE_LEN 4096\n+\n+void fast_export_delete(uint32_t depth, uint32_t *path)\n+{\n+    putchar('D');\n+    putchar(' ');\n+    pool_print_seq(depth, path, '/', stdout);\n+    putchar('\\n');\n+}\n+\n+void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,\n+                        uint32_t mark)\n+{\n+    printf(\"M %06o :%d \", mode, mark);\n+    pool_print_seq(depth, path, '/', stdout);\n+    putchar('\\n');\n+}\n+\n+static char gitsvnline[MAX_GITSVN_LINE_LEN];\n+void fast_export_commit(uint32_t revision, char *author, char *log,\n+                        char *uuid, char *url, time_t timestamp)\n+{\n+    if (!author)\n+        author = \"nobody\";\n+    if (!log)\n+        log = \"\";\n+    if (uuid && url) {\n+        snprintf(gitsvnline, MAX_GITSVN_LINE_LEN, \"\\n\\ngit-svn-id: %s@%d %s\\n\",\n+             url, revision, uuid);\n+    } else {\n+        *gitsvnline = '\\0';\n+    }\n+    printf(\"commit refs/heads/master\\nmark :%d\\n\", revision);\n+    printf(\"committer %s <%s@%s> %ld +0000\\n\",\n+         author, author, uuid ? uuid : \"local\", timestamp);\n+    printf(\"data %ld\\n%s%s\\n\",\n+           strlen(log) + strlen(gitsvnline), log, gitsvnline);\n+    repo_diff(revision - 1, revision);\n+    fputc('\\n', stdout);\n+\n+    printf(\"progress Imported commit %d.\\n\\n\", revision);\n+}\n+\n+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len)\n+{\n+    if (mode == REPO_MODE_LNK) {\n+        /* svn symlink blobs start with \"link \" */\n+        buffer_skip_bytes(5);\n+        len -= 5;\n+    }\n+    printf(\"blob\\nmark :%d\\ndata %d\\n\", mark, len);\n+    buffer_copy_bytes(len);\n+    fputc('\\n', stdout);\n+}\ndiff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h\nnew file mode 100644\nindex 0000000..e84144e\n--- /dev/null\n+++ b/vcs-svn/fast_export.h\n@@ -0,0 +1,17 @@\n+#ifndef FAST_EXPORT_H_\n+#define FAST_EXPORT_H_\n+\n+#include <stdint.h>\n+#include <time.h>\n+\n+void fast_export_delete(uint32_t depth, uint32_t *path);\n+\n+void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,\n+                        uint32_t mark);\n+\n+void fast_export_commit(uint32_t revision, char *author, char *log,\n+                        char *uuid, char *url, time_t timestamp);\n+\n+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len);\n+\n+#endif\ndiff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c\nnew file mode 100644\nindex 0000000..7c4a70f\n--- /dev/null\n+++ b/vcs-svn/repo_tree.c\n@@ -0,0 +1,333 @@\n+#include <string.h>\n+\n+#include \"string_pool.h\"\n+#include \"repo_tree.h\"\n+#include \"obj_pool.h\"\n+#include \"fast_export.h\"\n+\n+typedef struct repo_dirent_s repo_dirent_t;\n+\n+struct repo_dirent_s {\n+    uint32_t name_offset;\n+    uint32_t mode;\n+    uint32_t content_offset;\n+};\n+\n+typedef struct repo_dir_s repo_dir_t;\n+\n+struct repo_dir_s {\n+    uint32_t size;\n+    uint32_t first_offset;\n+};\n+\n+typedef struct repo_commit_s repo_commit_t;\n+\n+struct repo_commit_s {\n+    uint32_t mark;\n+    uint32_t root_dir_offset;\n+};\n+\n+/* Generate memory pools for commit, dir and dirent */\n+obj_pool_gen(commit, repo_commit_t, 4096);\n+obj_pool_gen(dir, repo_dir_t, 4096);\n+obj_pool_gen(dirent, repo_dirent_t, 4096);\n+\n+static uint32_t num_dirs_saved = 0;\n+static uint32_t num_dirents_saved = 0;\n+static uint32_t active_commit = -1;\n+\n+static repo_dir_t *repo_commit_root_dir(repo_commit_t *commit)\n+{\n+    return dir_pointer(commit->root_dir_offset);\n+}\n+\n+static repo_dirent_t *repo_first_dirent(repo_dir_t *dir)\n+{\n+    return dirent_pointer(dir->first_offset);\n+}\n+\n+static int repo_dirent_name_cmp(const void *a, const void *b)\n+{\n+    return (((repo_dirent_t *) a)->name_offset\n+            > ((repo_dirent_t *) b)->name_offset) -\n+        (((repo_dirent_t *) a)->name_offset\n+         < ((repo_dirent_t *) b)->name_offset);\n+}\n+\n+static repo_dirent_t *repo_dirent_by_name(repo_dir_t *dir,\n+                                          uint32_t name_offset)\n+{\n+    repo_dirent_t key;\n+    if (dir == NULL || dir->size == 0)\n+        return NULL;\n+    key.name_offset = name_offset;\n+    return bsearch(&key, repo_first_dirent(dir), dir->size,\n+                   sizeof(repo_dirent_t), repo_dirent_name_cmp);\n+}\n+\n+static int repo_dirent_is_dir(repo_dirent_t *dirent)\n+{\n+    return dirent != NULL && dirent->mode == REPO_MODE_DIR;\n+}\n+\n+static repo_dir_t *repo_dir_from_dirent(repo_dirent_t *dirent)\n+{\n+    if (!repo_dirent_is_dir(dirent))\n+        return NULL;\n+    return dir_pointer(dirent->content_offset);\n+}\n+\n+static uint32_t dir_with_dirents_alloc(uint32_t size)\n+{\n+    uint32_t offset = dir_alloc(1);\n+    dir_pointer(offset)->size = size;\n+    dir_pointer(offset)->first_offset = dirent_alloc(size);\n+    return offset;\n+}\n+\n+static repo_dir_t *repo_clone_dir(repo_dir_t *orig_dir, uint32_t padding)\n+{\n+    uint32_t orig_o, new_o, dirent_o;\n+    orig_o = dir_offset(orig_dir);\n+    if (orig_o < num_dirs_saved) {\n+        new_o = dir_with_dirents_alloc(orig_dir->size + padding);\n+        orig_dir = dir_pointer(orig_o);\n+        dirent_o = dir_pointer(new_o)->first_offset;\n+    } else {\n+        if (padding == 0)\n+            return orig_dir;\n+        new_o = orig_o;\n+        dirent_o = dirent_alloc(orig_dir->size + padding);\n+    }\n+    memcpy(dirent_pointer(dirent_o), repo_first_dirent(orig_dir),\n+           orig_dir->size * sizeof(repo_dirent_t));\n+    dir_pointer(new_o)->size = orig_dir->size + padding;\n+    dir_pointer(new_o)->first_offset = dirent_o;\n+    return dir_pointer(new_o);\n+}\n+\n+static char repo_path_buffer[REPO_MAX_PATH_LEN];\n+static repo_dirent_t *repo_read_dirent(uint32_t revision, char *path)\n+{\n+    char *ctx = NULL;\n+    uint32_t name = 0;\n+    repo_dir_t *dir = NULL;\n+    repo_dirent_t *dirent = NULL;\n+    dir = repo_commit_root_dir(commit_pointer(revision));\n+    strncpy(repo_path_buffer, path, REPO_MAX_PATH_LEN);\n+    repo_path_buffer[REPO_MAX_PATH_LEN - 1] = '\\0';\n+    path = repo_path_buffer;\n+    for (name = pool_tok_r(path, \"/\", &ctx);\n+         ~name; name = pool_tok_r(NULL, \"/\", &ctx)) {\n+        dirent = repo_dirent_by_name(dir, name);\n+        if (dirent == NULL) {\n+            return NULL;\n+        } else if (repo_dirent_is_dir(dirent)) {\n+            dir = repo_dir_from_dirent(dirent);\n+        } else {\n+            break;\n+        }\n+    }\n+    return dirent;\n+}\n+\n+static void\n+repo_write_dirent(char *path, uint32_t mode, uint32_t content_offset,\n+                  uint32_t del)\n+{\n+    char *ctx;\n+    uint32_t name, revision, dirent_o, dir_o, parent_dir_o;\n+    repo_dir_t *dir;\n+    repo_dirent_t *dirent = NULL;\n+    revision = active_commit;\n+    dir = repo_commit_root_dir(commit_pointer(revision));\n+    dir = repo_clone_dir(dir, 0);\n+    commit_pointer(revision)->root_dir_offset = dir_offset(dir);\n+    strncpy(repo_path_buffer, path, REPO_MAX_PATH_LEN);\n+    repo_path_buffer[REPO_MAX_PATH_LEN - 1] = '\\0';\n+    path = repo_path_buffer;\n+    for (name = pool_tok_r(path, \"/\", &ctx); ~name;\n+         name = pool_tok_r(NULL, \"/\", &ctx)) {\n+        parent_dir_o = dir_offset(dir);\n+        dirent = repo_dirent_by_name(dir, name);\n+        if (dirent == NULL) {\n+            dir = repo_clone_dir(dir, 1);\n+            dirent = &repo_first_dirent(dir)[dir->size - 1];\n+            dirent->name_offset = name;\n+            dirent->mode = REPO_MODE_DIR;\n+            qsort(repo_first_dirent(dir), dir->size,\n+                  sizeof(repo_dirent_t), repo_dirent_name_cmp);\n+            dirent = repo_dirent_by_name(dir, name);\n+            dir_o = dir_with_dirents_alloc(0);\n+            dirent->content_offset = dir_o;\n+            dir = dir_pointer(dir_o);\n+        } else if ((dir = repo_dir_from_dirent(dirent))) {\n+            dirent_o = dirent_offset(dirent);\n+            dir = repo_clone_dir(dir, 0);\n+            if (dirent_o != ~0)\n+                dirent_pointer(dirent_o)->content_offset = dir_offset(dir);\n+        } else {\n+            dirent->mode = REPO_MODE_DIR;\n+            dirent_o = dirent_offset(dirent);\n+            dir_o = dir_with_dirents_alloc(0);\n+            dirent = dirent_pointer(dirent_o);\n+            dir = dir_pointer(dir_o);\n+            dirent->content_offset = dir_o;\n+        }\n+    }\n+    if (dirent) {\n+        dirent->mode = mode;\n+        dirent->content_offset = content_offset;\n+        if (del && ~parent_dir_o) {\n+            dirent->name_offset = ~0;\n+            dir = dir_pointer(parent_dir_o);\n+            qsort(repo_first_dirent(dir), dir->size,\n+                  sizeof(repo_dirent_t), repo_dirent_name_cmp);\n+            dir->size--;\n+        }\n+    }\n+}\n+\n+uint32_t repo_copy(uint32_t revision, char *src, char *dst)\n+{\n+    uint32_t mode = 0, content_offset = 0;\n+    repo_dirent_t *src_dirent;\n+    src_dirent = repo_read_dirent(revision, src);\n+    if (src_dirent != NULL) {\n+        mode = src_dirent->mode;\n+        content_offset = src_dirent->content_offset;\n+        repo_write_dirent(dst, mode, content_offset, 0);\n+    }\n+    return mode;\n+}\n+\n+void repo_add(char *path, uint32_t mode, uint32_t blob_mark)\n+{\n+    repo_write_dirent(path, mode, blob_mark, 0);\n+}\n+\n+uint32_t repo_replace(char *path, uint32_t blob_mark)\n+{\n+    uint32_t mode = 0;\n+    repo_dirent_t *src_dirent;\n+    src_dirent = repo_read_dirent(active_commit, path);\n+    if (src_dirent != NULL) {\n+        mode = src_dirent->mode;\n+        repo_write_dirent(path, mode, blob_mark, 0);\n+    }\n+    return mode;\n+}\n+\n+void repo_modify(char *path, uint32_t mode, uint32_t blob_mark)\n+{\n+    repo_write_dirent(path, mode, blob_mark, 0);\n+}\n+\n+void repo_delete(char *path)\n+{\n+    repo_write_dirent(path, 0, 0, 1);\n+}\n+\n+static void\n+repo_git_add_r(uint32_t depth, uint32_t *path, repo_dir_t *dir);\n+\n+static void\n+repo_git_add(uint32_t depth, uint32_t *path, repo_dirent_t *dirent)\n+{\n+    if (repo_dirent_is_dir(dirent)) {\n+        repo_git_add_r(depth, path, repo_dir_from_dirent(dirent));\n+    } else {\n+        fast_export_modify(depth, path, dirent->mode, dirent->content_offset);\n+    }\n+}\n+\n+static void\n+repo_git_add_r(uint32_t depth, uint32_t *path, repo_dir_t *dir)\n+{\n+    uint32_t o;\n+    repo_dirent_t *de;\n+    de = repo_first_dirent(dir);\n+    for (o = 0; o < dir->size; o++) {\n+        path[depth] = de[o].name_offset;\n+        repo_git_add(depth + 1, path, &de[o]);\n+    }\n+}\n+\n+static void\n+repo_diff_r(uint32_t depth, uint32_t *path, repo_dir_t *dir1,\n+            repo_dir_t *dir2)\n+{\n+    repo_dirent_t *de1, *de2, *max_de1, *max_de2;\n+    de1 = repo_first_dirent(dir1);\n+    de2 = repo_first_dirent(dir2);\n+    max_de1 = &de1[dir1->size];\n+    max_de2 = &de2[dir2->size];\n+\n+    while (de1 < max_de1 && de2 < max_de2) {\n+        if (de1->name_offset < de2->name_offset) {\n+            path[depth] = (de1++)->name_offset;\n+            fast_export_delete(depth + 1, path);\n+        } else if (de1->name_offset == de2->name_offset) {\n+            path[depth] = de1->name_offset;\n+            if (de1->content_offset != de2->content_offset) {\n+                if (repo_dirent_is_dir(de1) && repo_dirent_is_dir(de2)) {\n+                    repo_diff_r(depth + 1, path,\n+                                repo_dir_from_dirent(de1),\n+                                repo_dir_from_dirent(de2));\n+                } else {\n+                    if (repo_dirent_is_dir(de1) != repo_dirent_is_dir(de2)) {\n+                        fast_export_delete(depth + 1, path);\n+                    }\n+                    repo_git_add(depth + 1, path, de2);\n+                }\n+            }\n+            de1++;\n+            de2++;\n+        } else {\n+            path[depth] = de2->name_offset;\n+            repo_git_add(depth + 1, path, de2++);\n+        }\n+    }\n+    while (de1 < max_de1) {\n+        path[depth] = (de1++)->name_offset;\n+        fast_export_delete(depth + 1, path);\n+    }\n+    while (de2 < max_de2) {\n+        path[depth] = de2->name_offset;\n+        repo_git_add(depth + 1, path, de2++);\n+    }\n+}\n+\n+static uint32_t path_stack[1000];\n+void repo_diff(uint32_t r1, uint32_t r2)\n+{\n+    repo_diff_r(0,\n+                path_stack,\n+                repo_commit_root_dir(commit_pointer(r1)),\n+                repo_commit_root_dir(commit_pointer(r2)));\n+}\n+\n+void repo_commit(uint32_t revision, char *author, char *log, char *uuid,\n+                 char *url, time_t timestamp)\n+{\n+    if (revision == 0) {\n+        active_commit = commit_alloc(1);\n+        commit_pointer(active_commit)->root_dir_offset =\n+            dir_with_dirents_alloc(0);\n+    } else {\n+        fast_export_commit(revision, author, log, uuid, url, timestamp);\n+    }\n+    num_dirs_saved = dir_pool.size;\n+    num_dirents_saved = dirent_pool.size;\n+    active_commit = commit_alloc(1);\n+    commit_pointer(active_commit)->root_dir_offset =\n+        commit_pointer(active_commit - 1)->root_dir_offset;\n+}\n+\n+void repo_reset(void)\n+{\n+    pool_reset();\n+    commit_reset();\n+    dir_reset();\n+    dirent_reset();\n+}\ndiff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h\nnew file mode 100644\nindex 0000000..2d645dc\n--- /dev/null\n+++ b/vcs-svn/repo_tree.h\n@@ -0,0 +1,31 @@\n+#ifndef REPO_TREE_H_\n+#define REPO_TREE_H_\n+\n+#include <stdint.h>\n+#include <time.h>\n+\n+#define REPO_MODE_DIR 0040000\n+#define REPO_MODE_BLB 0100644\n+#define REPO_MODE_EXE 0100755\n+#define REPO_MODE_LNK 0120000\n+\n+#define REPO_MAX_PATH_LEN 4096\n+\n+uint32_t repo_copy(uint32_t revision, char *src, char *dst);\n+\n+void repo_add(char *path, uint32_t mode, uint32_t blob_mark);\n+\n+uint32_t repo_replace(char *path, uint32_t blob_mark);\n+\n+void repo_modify(char *path, uint32_t mode, uint32_t blob_mark);\n+\n+void repo_delete(char *path);\n+\n+void repo_commit(uint32_t revision, char *author, char *log, char *uuid,\n+                 char *url, time_t timestamp);\n+\n+void repo_diff(uint32_t r1, uint32_t r2);\n+\n+void repo_reset(void);\n+\n+#endif\n-- \n1.7.1\n"},{"id":"142166","messageId":"1274650832-7411-8-git-send-email-artagnon@gmail.com","threadId":"23884","inReplyTo":"1274650832-7411-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 7/7] Add handler for SVN dump","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-23T21:40:32Z","receivedAt":"2010-05-23T21:40:32Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Expose an API to parse an SVN dump created with `svnadmin dump` and\nemit a fast-import stream using fast_export and repo_tree. This is a\ntemporary workaround: it will be replaced with a client that\ncommunicates live with a remote SVN server in future. Taken directly\nfrom David Michael Barr's svn-dump-fast-export repository.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n vcs-svn/svndump.c |  277 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n vcs-svn/svndump.h |    7 ++\n 2 files changed, 284 insertions(+), 0 deletions(-)\n create mode 100644 vcs-svn/svndump.c\n create mode 100644 vcs-svn/svndump.h\n\ndiff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c\nnew file mode 100644\nindex 0000000..dbe8d77\n--- /dev/null\n+++ b/vcs-svn/svndump.c\n@@ -0,0 +1,277 @@\n+/*\n+ * Parse and rearrange a svnadmin dump.\n+ * Create the dump with:\n+ * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile\n+ */\n+\n+#define _GNU_SOURCE\n+#include <string.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <time.h>\n+\n+#include \"repo_tree.h\"\n+#include \"fast_export.h\"\n+#include \"line_buffer.h\"\n+\n+#define NODEACT_REPLACE 4\n+#define NODEACT_DELETE 3\n+#define NODEACT_ADD 2\n+#define NODEACT_CHANGE 1\n+#define NODEACT_UNKNOWN 0\n+\n+#define DUMP_CTX 0\n+#define REV_CTX  1\n+#define NODE_CTX 2\n+\n+#define LENGTH_UNKNOWN (~0)\n+\n+#define BLOB_MARK_OFFSET 1000000000\n+\n+static struct {\n+    uint32_t action, propLength, textLength, srcRev, srcMode, mark, type;\n+    char *src, *dst;\n+} node_ctx;\n+\n+static struct {\n+    uint32_t revision;\n+    time_t timestamp;\n+    char *descr, *author, *date;\n+} rev_ctx;\n+\n+static struct {\n+    char *uuid, *url;\n+} dump_ctx;\n+\n+static void reset_node_ctx(char *fname)\n+{\n+    node_ctx.type = 0;\n+    node_ctx.action = NODEACT_UNKNOWN;\n+    node_ctx.propLength = LENGTH_UNKNOWN;\n+    node_ctx.textLength = LENGTH_UNKNOWN;\n+    if (node_ctx.src)\n+        free(node_ctx.src);\n+    node_ctx.src = NULL;\n+    node_ctx.srcRev = 0;\n+    node_ctx.srcMode = 0;\n+    if (node_ctx.dst)\n+        free(node_ctx.dst);\n+    node_ctx.dst = fname;\n+    node_ctx.mark = 0;\n+}\n+\n+static void reset_rev_ctx(uint32_t revision)\n+{\n+    rev_ctx.revision = revision;\n+    rev_ctx.timestamp = 0;\n+    if (rev_ctx.descr)\n+        free(rev_ctx.descr);\n+    rev_ctx.descr = NULL;\n+    if (rev_ctx.author)\n+        free(rev_ctx.author);\n+    rev_ctx.author = NULL;\n+    if (rev_ctx.date)\n+        free(rev_ctx.date);\n+    rev_ctx.date = NULL;\n+}\n+\n+static void reset_dump_ctx(char *url)\n+{\n+    if (dump_ctx.url)\n+        free(dump_ctx.url);\n+    dump_ctx.url = url;\n+    if (dump_ctx.uuid)\n+        free(dump_ctx.uuid);\n+    dump_ctx.uuid = NULL;\n+}\n+\n+static uint32_t next_blob_mark(void)\n+{\n+    static uint32_t mark = BLOB_MARK_OFFSET;\n+    return mark++;\n+}\n+\n+static void read_props(void)\n+{\n+    struct tm tm;\n+    uint32_t len;\n+    char *key = NULL;\n+    char *val = NULL;\n+    char *t;\n+    while ((t = buffer_read_line()) && strcmp(t, \"PROPS-END\")) {\n+        if (!strncmp(t, \"K \", 2)) {\n+            len = atoi(&t[2]);\n+            key = buffer_read_string(len);\n+            buffer_read_line();\n+        } else if (!strncmp(t, \"V \", 2)) {\n+            len = atoi(&t[2]);\n+            val = buffer_read_string(len);\n+            if (!strcmp(key, \"svn:log\")) {\n+                if (rev_ctx.descr)\n+                    free(rev_ctx.descr);\n+                rev_ctx.descr = val;\n+            } else if (!strcmp(key, \"svn:author\")) {\n+                if (rev_ctx.author)\n+                    free(rev_ctx.author);\n+                rev_ctx.author = val;\n+            } else if (!strcmp(key, \"svn:date\")) {\n+                if (rev_ctx.date)\n+                    free(rev_ctx.date);\n+                rev_ctx.date = val;\n+                strptime(rev_ctx.date, \"%FT%T\", &tm);\n+                timezone = 0;\n+                tm.tm_isdst = 0;\n+                rev_ctx.timestamp = mktime(&tm);\n+            } else if (!strcmp(key, \"svn:executable\")) {\n+                if (node_ctx.type == REPO_MODE_BLB) {\n+                    node_ctx.type = REPO_MODE_EXE;\n+                }\n+                free(val);\n+            } else if (!strcmp(key, \"svn:special\")) {\n+                if (node_ctx.type == REPO_MODE_BLB) {\n+                    node_ctx.type = REPO_MODE_LNK;\n+                }\n+                free(val);\n+            } else {\n+                free(val);\n+            }\n+            if (key)\n+                free(key);\n+            key = NULL;\n+            buffer_read_line();\n+        }\n+    }\n+}\n+\n+static void handle_node(void)\n+{\n+    if (node_ctx.propLength != LENGTH_UNKNOWN && node_ctx.propLength) {\n+        read_props();\n+    }\n+\n+    if (node_ctx.src && node_ctx.srcRev) {\n+        node_ctx.srcMode =\n+            repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst);\n+    }\n+\n+    if (node_ctx.textLength != LENGTH_UNKNOWN &&\n+        node_ctx.type != REPO_MODE_DIR) {\n+        node_ctx.mark = next_blob_mark();\n+    }\n+\n+    if (node_ctx.action == NODEACT_DELETE) {\n+        repo_delete(node_ctx.dst);\n+    } else if (node_ctx.action == NODEACT_CHANGE || \n+               node_ctx.action == NODEACT_REPLACE) {\n+        if (node_ctx.propLength != LENGTH_UNKNOWN &&\n+            node_ctx.textLength != LENGTH_UNKNOWN) {\n+            repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);\n+        } else if (node_ctx.textLength != LENGTH_UNKNOWN) {\n+            node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);\n+        }\n+    } else if (node_ctx.action == NODEACT_ADD) {\n+        if (node_ctx.src && node_ctx.srcRev &&\n+            node_ctx.propLength == LENGTH_UNKNOWN &&\n+            node_ctx.textLength != LENGTH_UNKNOWN) {\n+            node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);\n+        } else if (node_ctx.type == REPO_MODE_DIR ||\n+                   node_ctx.textLength != LENGTH_UNKNOWN){\n+            repo_add(node_ctx.dst, node_ctx.type, node_ctx.mark);\n+        }\n+    }\n+\n+    if (node_ctx.propLength == LENGTH_UNKNOWN && node_ctx.srcMode) {\n+        node_ctx.type = node_ctx.srcMode;\n+    }\n+\n+    if (node_ctx.mark) {\n+        fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength);\n+    } else if (node_ctx.textLength != LENGTH_UNKNOWN) {\n+        buffer_skip_bytes(node_ctx.textLength);\n+    }\n+}\n+\n+static void handle_revision(void)\n+{\n+    repo_commit(rev_ctx.revision, rev_ctx.author, rev_ctx.descr, dump_ctx.uuid,\n+                dump_ctx.url, rev_ctx.timestamp);\n+}\n+\n+static void svndump_read(char *url)\n+{\n+    char *val;\n+    char *t;\n+    uint32_t active_ctx = DUMP_CTX; \n+    uint32_t len;\n+\n+    reset_dump_ctx(url);\n+    while ((t = buffer_read_line())) {\n+        val = strstr(t, \": \");\n+        if (!val) continue;\n+        *val++ = '\\0';\n+        *val++ = '\\0';\n+\n+        if(!strcmp(t, \"UUID\")) {\n+            dump_ctx.uuid = strdup(val);\n+        } else if (!strcmp(t, \"Revision-number\")) {\n+            if (active_ctx != DUMP_CTX) handle_revision();\n+            active_ctx = REV_CTX;\n+            reset_rev_ctx(atoi(val));\n+        } else if (!strcmp(t, \"Node-path\")) {\n+            active_ctx = NODE_CTX;\n+            reset_node_ctx(strdup(val));\n+        } else if (!strcmp(t, \"Node-kind\")) {\n+            if (!strcmp(val, \"dir\")) {\n+                node_ctx.type = REPO_MODE_DIR;\n+            } else if (!strcmp(val, \"file\")) {\n+                node_ctx.type = REPO_MODE_BLB;\n+            } else {\n+                fprintf(stderr, \"Unknown node-kind: %s\\n\", val);\n+            }\n+        } else if (!strcmp(t, \"Node-action\")) {\n+            if (!strcmp(val, \"delete\")) {\n+                node_ctx.action = NODEACT_DELETE;\n+            } else if (!strcmp(val, \"add\")) {\n+                node_ctx.action = NODEACT_ADD;\n+            } else if (!strcmp(val, \"change\")) {\n+                node_ctx.action = NODEACT_CHANGE;\n+            } else if (!strcmp(val, \"replace\")) {\n+                node_ctx.action = NODEACT_REPLACE;\n+            } else {\n+                fprintf(stderr, \"Unknown node-action: %s\\n\", val);\n+                node_ctx.action = NODEACT_UNKNOWN;\n+            }\n+        } else if (!strcmp(t, \"Node-copyfrom-path\")) {\n+            if (node_ctx.src)\n+                free(node_ctx.src);\n+            node_ctx.src = strdup(val);\n+        } else if (!strcmp(t, \"Node-copyfrom-rev\")) {\n+            node_ctx.srcRev = atoi(val);\n+        } else if (!strcmp(t, \"Text-content-length\")) {\n+            node_ctx.textLength = atoi(val);\n+        } else if (!strcmp(t, \"Prop-content-length\")) {\n+            node_ctx.propLength = atoi(val);\n+        } else if (!strcmp(t, \"Content-length\")) {\n+            len = atoi(val);\n+            buffer_read_line();\n+            if (active_ctx == REV_CTX) {\n+                read_props();\n+            } else if (active_ctx == NODE_CTX) {\n+                handle_node();\n+                active_ctx = REV_CTX;\n+            } else {\n+                fprintf(stderr, \"Unexpected content length header: %d\\n\", len);\n+                buffer_skip_bytes(len);\n+            }\n+        }\n+    } \n+    if (active_ctx != DUMP_CTX) handle_revision();\n+}\n+\n+static void svndump_reset(void)\n+{\n+    repo_reset();\n+    reset_dump_ctx(NULL);\n+    reset_rev_ctx(0);\n+    reset_node_ctx(NULL);\n+}\ndiff --git a/vcs-svn/svndump.h b/vcs-svn/svndump.h\nnew file mode 100644\nindex 0000000..b11f666\n--- /dev/null\n+++ b/vcs-svn/svndump.h\n@@ -0,0 +1,7 @@\n+#ifndef SVNDUMP_H_\n+#define SVNDUMP_H_\n+\n+static void svndump_read(char *url);\n+static void svndump_reset(void);\n+\n+#endif\n-- \n1.7.1\n"},{"id":"142179","messageId":"20100524074757.GC3412@m62s10.vlinux.de","threadId":"23884","inReplyTo":"1274650832-7411-4-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 3/7] Add buffer pool library","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2010-05-24T07:47:57Z","receivedAt":"2010-05-24T07:47:57Z","isPatch":true,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Sun, May 23, 2010 at 11:40:28PM +0200, Ramkumar Ramachandra wrote:\n> line_buffer creates a couple of static buffers and expose an API for\n> using them. The idea is to maintain a fixed static memory pool to\n> avoid constant allocation and de-allocation of memory. Taken directly\n> from David Michael Barr's svn-dump-fast-export repository.\n> \n\nIf this is David's code, shouldn't you then preserve authorship?\n"},{"id":"142195","messageId":"AANLkTim8olZLKoi4Uk_nPpxhCYKX7b_yUDBrWVW_rm57@mail.gmail.com","threadId":"23884","inReplyTo":"20100524074757.GC3412@m62s10.vlinux.de","subject":"Re: [PATCH 3/7] Add buffer pool library","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-24T10:11:37Z","receivedAt":"2010-05-24T10:11:37Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nOn Mon, May 24, 2010 at 9:47 AM, Peter Baumann <waste.manager@gmx.de> wrote:\n> If this is David's code, shouldn't you then preserve authorship?\n\nYeah, I thought about that- can we hand-edit the author since atleast\n90% of the code is David's? What are your thoughts on importing some\nof the revision history from the original repository?\n@David: I'll take the responsibility of cleaning it up/ modifying it\nand getting it merged into git.git. You can work on the repository\nindependently, and I'll pull relevant changes into git.git.\n\n-- Ram\n"},{"id":"142197","messageId":"59ECADA1-5F48-4C0E-B3C5-C2F7D7687C8F@cordelta.com","threadId":"23884","inReplyTo":"AANLkTim8olZLKoi4Uk_nPpxhCYKX7b_yUDBrWVW_rm57@mail.gmail.com","subject":"Re: [PATCH 3/7] Add buffer pool library","fromName":"David Michael Barr","fromEmail":"david.barr@cordelta.com","sentAt":"2010-05-24T10:37:59Z","receivedAt":"2010-05-24T10:37:59Z","isPatch":true,"sender":{"key":"david.barr@cordelta.com","avatar":"https://avatars.githubusercontent.com/u/220594?v=4"},"body":"Hi,\n\n> @David: I'll take the responsibility of cleaning it up/ modifying it\n> and getting it merged into git.git. You can work on the repository\n> independently, and I'll pull relevant changes into git.git.\n\n@Ram: On that note, can you sign off on your patches so I can\nmerge them.\n\n>> If this is David's code, shouldn't you then preserve authorship?\n\n\n[PATCH 2/7] Add cpp macro implementation of treaps\nAuthored mostly by Jason Evans.\nHeavily culled and adapted to use the object pool library by\nDavid Barr.\n\n[PATCH 7/7] Add handler for SVN dump\nOriginally authored by in Java by Stefan Hegny and others.\nPorted to C and rewritten by David Barr.\n\nBoth released under two clause BSD licenses.\n\nPatches 3 to 6 authored primarily by David Barr with\ncontributions in the form of cleanup and documentation from\nRamkumar Ramachadra.\nReleased under two-clause BSD license but easily relicensed\ndue to proximity of authors.\n\n-- David Barr.\n"},{"id":"142526","messageId":"20100529071811.GA6648@progeny.tock","threadId":"23884","inReplyTo":"1274650832-7411-3-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 2/7] Add cpp macro implementation of treaps","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-29T07:18:12Z","receivedAt":"2010-05-29T07:18:12Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Ram,\n\nRamkumar Ramachandra wrote:\n\n> Taken directly\n> from David Michael Barr's svn-dump-fast-export repository.\n\nMore details:\n\n  From: Jason Evans <jasone@canonware.com>\n\n  Treaps provide a memory-efficient binary search tree structure.\n  Insertion/deletion/search are about as about as fast in the average\n  case as red-black trees and the chances of worst-case behavior are\n  vanishingly small, thanks to (pseudo-)randomness.  That is a small\n  price to pay, given that treaps are much simpler to implement.\n\n  [db: Altered to reference nodes by offset from a common base pointer]\n  [db: Bob Jenkins' hashing implementation dropped for Knuth's]\n  [db: Methods unnecessary for search and insert dropped]\n\n  Signed-off-by: David Barr <david.barr@cordelta.com>\n  Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n\nI stole most of the description from [1].\n\n> --- /dev/null\n> +++ b/vcs-svn/trp.h\n> @@ -0,0 +1,221 @@\n> +/******************************************************************************\n> + *\n> + * cpp macro implementation of treaps.\n> + *\n> + * Usage:\n> + *\n> + *   #include <trp.h>\n> + *   trp(...)\n> + *   trp_gen(...)\n> + *   ...\n> + *\n> + ******************************************************************************/\n\nI don’t know if style nitpicking is welcome here, given that the\ncode comes from elsewhere.  Should we be trying to keep the code\nclose to Jason’s version (and sending patches upstream as needed),\nor is that not worth the trouble?\n\nIf style nitpicking were appropriate, I would say that comments in\nC code written specifically for git should look like this:\n\n   /*\n    * cpp macro implementation of treaps.\n   [...]\n    */\n\nOkay. :)\n\n> +\n> +#ifndef TRP_H_\n> +#define\tTRP_H_\n> +\n> +#include <stdint.h>\n\n#include \"../git-compat-util.h\" would be more portable.\n\n> +/* Pointer/Offset conversion */\n> +#define trpn_pointer(a_base, a_offset)\t\t\t\t\t\\\n> +    (a_base##_pointer(a_offset))\n> +#define trpn_offset(a_base, a_pointer)\t\t\t\t        \\\n> +    (a_base##_offset(a_pointer))\n\nThese are defined in obj_pool.h?  Maybe this would be easier to\nunderstand if the patch to add that file came sooner in the series.\n\n> +/* Priority accessors. */\n> +#define\ttrp_prio_get(a_type, a_field, a_node)\t\t\t\t\\\n> +    (2654435761*(uint32_t)(uintptr_t)(a_node))\n\nWhere does this magic number come from?  (I assume Knuth, but it\nwould be nice to include a reference or explanation.)\n\n> +/*\n> + * The trp_gen() macro generates a type-specific treap implementation,\n> + * based on the above cpp macros.\n[...]\n> + * Assuming the following setup:\n> + *\n> + *   typedef struct ex_node_s ex_node_t;\n> + *   struct ex_node_s {\n> + *       trp_node(ex_node_t) ex_link;\n> + *   };\n> + *   typedef trp(ex_node_t) ex_t;\n> + *   static ex_node_t ex_base[MAX_NODES];\n> + *   trp_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_base, ex_cmp)\n> + *\n> + * The following API is generated:\n> + *\n> + *   static void\n> + *   ex_new(ex_t *treap);\n> + *       Description: Initialize a treap structure.\n> + *       Args:\n> + *         treap: Pointer to an uninitialized treap object.\n> + *\n> + *   static ex_node_t *\n> + *   ex_psearch(ex_t *treap, ex_node_t *key);\n> + *       Description: Search for node that matches key.  If no match is found,\n> + *                    return what would be key's successor/predecessor, were\n> + *                    key in treap.\n> + *       Args:\n> + *         treap: Pointer to a initialized treap object.\n> + *         key  : Search key.\n> + *       Ret: Node in treap that matches key, or if no match, hypothetical\n> + *            node's successor/predecessor (NULL if no successor/predecessor).\n[...]\n\nNeat.  Maybe this description should go in a file in\nDocumentation/technical, to make trp.h itself a little less daunting.\n\nAlso: http://www.canonware.com/trp/ seems to provide a test program;\ndo you think it would make sense to include it as well?\n\nThanks,\nJonathan\n\n[1] http://t-t-travails.blogspot.com/2008/07/treaps-versus-red-black-trees.html\n"},{"id":"142528","messageId":"20100529085124.GA6847@progeny.tock","threadId":"23884","inReplyTo":"1274650832-7411-4-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 3/7] Add buffer pool library","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-29T08:51:24Z","receivedAt":"2010-05-29T08:51:24Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Ram,\n\nRamkumar Ramachandra wrote:\n\n> line_buffer creates a couple of static buffers and expose an API for\n> using them.\n\nSo this provides a thread-unsafe fgets() and fread() where the caller\ndoes not have to supply a buffer.  Sounds convenient.\n\n> Taken directly\n> from David Michael Barr's svn-dump-fast-export repository.\n>\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n\nMissing From: line and sign-off.\n\n[...]\n> +char *buffer_read_line(void)\n> +{\n> +    char *end;\n\nstyle nitpick: use tabs to indent.\n\n> +    uint32_t n_read;\n> +\n> +    if (line_len) {\n> +        memmove(line_buffer, &line_buffer[line_len],\n> +                line_buffer_len - line_len);\n> +        line_buffer_len -= line_len;\n> +        line_len = 0;\n> +    }\n> +\n> +    end = memchr(line_buffer, '\\n', line_buffer_len);\n> +    while (line_buffer_len < LINE_BUFFER_LEN - 1 &&\n> +           !feof(stdin) && NULL == end) {\n> +        n_read =\n> +            fread(&line_buffer[line_buffer_len], 1,\n> +                  LINE_BUFFER_LEN - 1 - line_buffer_len,\n> +                  stdin);\n> +        end = memchr(&line_buffer[line_buffer_len], '\\n', n_read);\n> +        line_buffer_len += n_read;\n> +    }\n\nWhy not fgets()?\n\n[...]\n> +char *buffer_read_string(uint32_t len)\n> +{\n> +    char *s = malloc(len + 1);\n> +    uint32_t offset = 0;\n> +    if (line_buffer_len > line_len) {\n> +        offset = line_buffer_len - line_len;\n> +        if (offset > len)\n> +            offset = len;\n> +        memcpy(s, &line_buffer[line_len], offset);\n\nSo if this buffer library is in use, all input needs to pass through\nit?  I would prefer to avoid that if possible.\n\n> +        line_len += offset;\n> +    }\n> +    while (offset < len && !feof(stdin)) {\n> +        offset += fread(&s[offset], 1, len - offset, stdin);\n> +    }\n\nOn error, wouldn’t this be an infinite loop?  Maybe:\n\n  offset += fread(&s[offset], 1, len - offset, stdin);\n  if (ferror(stdin)) {\n\tfree(s);\n\treturn NULL;\n }\n\nOne iteration should be sufficient, since fread loops internally.\n\n[...]\n> +void buffer_copy_bytes(uint32_t len)\n> +{\n> +    uint32_t in, out;\n> +    if (line_buffer_len > line_len) {\n> +        in = line_buffer_len - line_len;\n> +        if (in > len)\n> +            in = len;\n> +        out = 0;\n> +        while (out < in && !ferror(stdout)) {\n> +            out +=\n> +                fwrite(&line_buffer[line_len + out], 1, in - out, stdout);\n> +        }\n\nLikewise.\n\n> +        len -= in;\n> +        line_len += in;\n> +    }\n> +    while (len > 0 && !feof(stdin)) {\n> +        in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;\n> +        in = fread(byte_buffer, 1, in, stdin);\n> +        len -= in;\n> +        out = 0;\n> +        while (out < in && !ferror(stdout)) {\n> +            out += fwrite(&byte_buffer[out], 1, in - out, stdout);\n\nLikewise.\n\nWhy isn’t line_buffer used here?\n\n[...]\n> +void buffer_skip_bytes(uint32_t len)\n> +{\n[...]\n> +    while (len > 0 && !feof(stdin)) {\n> +        in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;\n> +        in = fread(byte_buffer, 1, in, stdin);\n> +        len -= in;\n> +    }\n\nLikewise.\n\nToo bad stdio does not supply a function for this (fseek is the\nclosest I can find, and it does not work on unseekable files).\n\nThanks,\nJonathan\n"},{"id":"142529","messageId":"20100529090609.GA7247@progeny.tock","threadId":"23884","inReplyTo":"1274650832-7411-5-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 4/7] Add a memory pool library","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-29T09:06:10Z","receivedAt":"2010-05-29T09:06:10Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Add a memory pool library implemented using cpp macros.\n\nHmm, like talloc[1].  How do they compare?\n\n[...]\n> + *   pre              : Prefix for generated functions (ex: string_).\n> + *   obj_t            : Type for treap data structure (ex: char).\n> + *   intial_capacity  : The initial size of the memory pool (ex: 4096).\n> + *\n> + */\n> +#define obj_pool_gen(pre, obj_t, initial_capacity)                         \\\n> +static struct {                                                            \\\n> +    uint32_t size;                                                         \\\n> +    uint32_t capacity;                                                     \\\n> +    obj_t *base;                                                           \\\n> +} pre##_pool = { 0, 0, NULL};                                              \\\n> +static uint32_t pre##_alloc(uint32_t count)                                \\\n[...]\n\nThis makes functions with names like string_alloc(), which would then\nbe un-greppable.  It might be nice (though certainly not necessary) to\nprovide an overview in Documentation/technical/ so there is no need to\nfind the definition of each function.\n\nJonathan\n\n[1] http://ccan.ozlabs.org/info/talloc.html\n"},{"id":"142530","messageId":"512B4EA0-463F-4CF2-94AB-404BF1BAE229@cordelta.com","threadId":"23884","inReplyTo":"20100529085124.GA6847@progeny.tock","subject":"Re: [PATCH 3/7] Add buffer pool library","fromName":"David Michael Barr","fromEmail":"david.barr@cordelta.com","sentAt":"2010-05-29T10:55:24Z","receivedAt":"2010-05-29T10:55:24Z","isPatch":true,"sender":{"key":"david.barr@cordelta.com","avatar":"https://avatars.githubusercontent.com/u/220594?v=4"},"body":"Ji Jonathan,\n\n>> line_buffer creates a couple of static buffers and expose an API for\n>> using them.\n> \n> So this provides a thread-unsafe fgets() and fread() where the caller\n> does not have to supply a buffer.  Sounds convenient.\n\nVery convenient indeed. Its primary reason for existence is to assist\ndebugging the svn dump parser.\n\n> Missing From: line and sign-off.\n\nI'm sure Ram will fix this up in the next rebase.\n\n>> +char *buffer_read_line(void)\n>> +{\n>> +    char *end;\n> \n> style nitpick: use tabs to indent.\n\nWill read git style guide and try to match for convenience.\n\n> Why not fgets()?\n\nHistorical reasons, can be factored out.\n\n> So if this buffer library is in use, all input needs to pass through\n> it?  I would prefer to avoid that if possible.\n\nThe design is almost single-caller anyway...\n\n>> +        line_len += offset;\n>> +    }\n>> +    while (offset < len && !feof(stdin)) {\n>> +        offset += fread(&s[offset], 1, len - offset, stdin);\n>> +    }\n> \n> On error, wouldn’t this be an infinite loop?  Maybe:\n> \n>  offset += fread(&s[offset], 1, len - offset, stdin);\n>  if (ferror(stdin)) {\n> \tfree(s);\n> \treturn NULL;\n> }\n> \n> One iteration should be sufficient, since fread loops internally\n\nThanks for the tip.\n\n--\nDavid Barr\n"},{"id":"142532","messageId":"20100529113800.GA7925@progeny.tock","threadId":"23884","inReplyTo":"1274650832-7411-6-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 5/7] Add API for string-specific memory pool","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-29T11:38:00Z","receivedAt":"2010-05-29T11:38:00Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> string_pool uses the macros in the memory pool library to create a\n> memory pool for strings and expose an API for handling the\n> strings.\n\nThis interns strings so they can be compared by address.  Interesting.\nThe use of offsets instead of pointers here mean it is possible to\nback the pool by a file, if I understand correctly.\n\n> Taken directly from David Michael Barr's svn-dump-fast-export\n> repository.\n\nMissing From: and sign-off.\n\n[...]\n> +static int node_indentity_cmp(node_t *a, node_t *b)\n> +{\n> +    int r = node_value_cmp(a, b);\n> +    return r ? r : (((uintptr_t) a) > ((uintptr_t) b))\n> +        - (((uintptr_t) a) < ((uintptr_t) b));\n\nnitpick: could use fewer parentheses.\n\n\treturn r ? r : (((uintptr_t) a > (uintptr_t) b) - ...\n\nAre the casts to uintptr_t necessary?  C99 says, under \"Relational\noperators\", paragraph 5:\n\n  When two pointers are compared, the result depends on the relative\n  locations in the address space of the objects pointed to. If two\n  pointers to object or incomplete types both point to the same\n  object, or both point one past the last element of the same array\n  object, they compare equal. If the objects pointed to are members of\n  the same aggregate object,\n  [etc]\n\nwhich seems to suggest that no, simple expressions like (a > b) should\nbe okay, but then it finishes with\n\n  In all other cases, the behavior is undefined.\n\nso I guess it is safer to keep the casts.\n\n> diff --git a/vcs-svn/string_pool.h b/vcs-svn/string_pool.h\n> new file mode 100644\n> index 0000000..fb9e6b8\n> --- /dev/null\n> +++ b/vcs-svn/string_pool.h\n> @@ -0,0 +1,11 @@\n> +#ifndef STRING_POOL_H_\n> +#define\tSTRING_POOL_H_\n\nstyle nitpick: should use space instead of tab\n\n> +\n> +#include <stdint.h>\n> +#include <stdio.h>\n\nShould use \"../git-compat-util.h\" for portability: unfortunately, some\nplatforms git supports still don’t have stdint.h iirc.\n\nExcept as noted above,\n\n  Reviewed-by: Jonathan Nieder <jrnieder@gmail.com>\n\nThanks for the pleasant read.\nJonathan\n"},{"id":"142534","messageId":"20100529140645.GA8338@progeny.tock","threadId":"23884","inReplyTo":"1274650832-7411-7-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 6/7] Add SVN revision parser and exporter","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-29T14:06:45Z","receivedAt":"2010-05-29T14:06:45Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi again,\n\nRamkumar Ramachandra wrote:\n\n> repo_tree parses SVN revisions to build a Git objects, and use\n> fast_export to emit them so they can be imported into the Git object\n> store via a fast-import.\n\nWait, where are SVN revisions being parsed?  It seems that the repo\nmodule maintains the exporter's state and provides a facility to\nto call the fast_export module to write it out.\n\n  repo_add(path, mode, blob_mark) is used to add a new file to\n  the current commit.\n\n  repo_modify is used to add a replacement for an existing file;\n  it is implemented exactly the same way, but a check could be\n  added later to distinguish the two cases.\n\n  repo_copy copies a blob from a previous revision to the current\n  commit.\n\n  repo_replace modifies the content of a file from the current\n  commit, if and only if it already exists.\n\n  repo_delete removes a file or directory from the current commit.\n\n  repo_commit calls out to fast_export to write the current commit\n  to the fast-import stream in stdout.\n\n  repo_diff is used by the fast_export module to write the changes\n  for a commit.\n\n  repo_reset erases the exporter's state, so valgrind can be happy.\n\n> +void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,\n> +                        uint32_t mark)\n> +{\n> +    printf(\"M %06o :%d \", mode, mark);\n> +    pool_print_seq(depth, path, '/', stdout);\n> +    putchar('\\n');\n> +}\n\nMode must be 100644, 100755, 120000, or 160000.\n\n[...]\n> +typedef struct repo_dirent_s repo_dirent_t;\n> +\n> +struct repo_dirent_s {\n> +    uint32_t name_offset;\n> +    uint32_t mode;\n> +    uint32_t content_offset;\n> +};\n> +\n> +typedef struct repo_dir_s repo_dir_t;\n> +\n> +struct repo_dir_s {\n> +    uint32_t size;\n> +    uint32_t first_offset;\n> +};\n> +\n> +typedef struct repo_commit_s repo_commit_t;\n> +\n> +struct repo_commit_s {\n> +    uint32_t mark;\n> +    uint32_t root_dir_offset;\n> +};\n\nStyle: we don’t tend to use typedef to hide underlying struct definitions\n(see Documentation/CodingStyle from linux-2.6.git, chapter 5, for some\nexplanation about why).\n\n> +static uint32_t num_dirs_saved = 0;\n> +static uint32_t num_dirents_saved = 0;\n\nStyle: leave out the initializer for bss-allocated variables.\n\n> +static int repo_dirent_name_cmp(const void *a, const void *b)\n> +{\n> +    return (((repo_dirent_t *) a)->name_offset\n> +            > ((repo_dirent_t *) b)->name_offset) -\n> +        (((repo_dirent_t *) a)->name_offset\n> +         < ((repo_dirent_t *) b)->name_offset);\n> +}\n\nMaybe some local variables would make this more readable:\n\n {\n\tconst repo_dirent_t *dirent1 = a, *dirent2 = b;\n\tuint32_t a_offset = dirent1->name_offset;\n\tuint32_t b_offset = dirent2->name_offset;\n\n\treturn (a_offset > b_offset) - (a_offset < b_offset);\n }\n\n> +static repo_dir_t *repo_clone_dir(repo_dir_t *orig_dir, uint32_t padding)\n> +{\n> +    uint32_t orig_o, new_o, dirent_o;\n> +    orig_o = dir_offset(orig_dir);\n> +    if (orig_o < num_dirs_saved) {\n> +        new_o = dir_with_dirents_alloc(orig_dir->size + padding);\n> +        orig_dir = dir_pointer(orig_o);\n> +        dirent_o = dir_pointer(new_o)->first_offset;\n> +    } else {\n> +        if (padding == 0)\n> +            return orig_dir;\n> +        new_o = orig_o;\n> +        dirent_o = dirent_alloc(orig_dir->size + padding);\n> +    }\n> +    memcpy(dirent_pointer(dirent_o), repo_first_dirent(orig_dir),\n> +           orig_dir->size * sizeof(repo_dirent_t));\n> +    dir_pointer(new_o)->size = orig_dir->size + padding;\n> +    dir_pointer(new_o)->first_offset = dirent_o;\n> +    return dir_pointer(new_o);\n> +}\n\nIs this for adding new entries to an existing directory?  It is\ngetting late, so I did not look it over carefully.\n\n> +static char repo_path_buffer[REPO_MAX_PATH_LEN];\n> +static repo_dirent_t *repo_read_dirent(uint32_t revision, char *path)\n> +{\n> +    char *ctx = NULL;\n> +    uint32_t name = 0;\n> +    repo_dir_t *dir = NULL;\n> +    repo_dirent_t *dirent = NULL;\n> +    dir = repo_commit_root_dir(commit_pointer(revision));\n> +    strncpy(repo_path_buffer, path, REPO_MAX_PATH_LEN);\n> +    repo_path_buffer[REPO_MAX_PATH_LEN - 1] = '\\0';\n> +    path = repo_path_buffer;\n> +    for (name = pool_tok_r(path, \"/\", &ctx);\n> +         ~name; name = pool_tok_r(NULL, \"/\", &ctx)) {\n> +        dirent = repo_dirent_by_name(dir, name);\n> +        if (dirent == NULL) {\n> +            return NULL;\n> +        } else if (repo_dirent_is_dir(dirent)) {\n> +            dir = repo_dir_from_dirent(dirent);\n> +        } else {\n> +            break;\n> +        }\n> +    }\n> +    return dirent;\n> +}\n\nIf a file \"foo\" exists and I ask for \"foo/bar\", this will return\nthe entry for foo.  Is that appropriate?\n\n> +static void\n> +repo_write_dirent(char *path, uint32_t mode, uint32_t content_offset,\n> +                  uint32_t del)\n> +{\n> +    char *ctx;\n> +    uint32_t name, revision, dirent_o, dir_o, parent_dir_o;\n> +    repo_dir_t *dir;\n> +    repo_dirent_t *dirent = NULL;\n> +    revision = active_commit;\n> +    dir = repo_commit_root_dir(commit_pointer(revision));\n> +    dir = repo_clone_dir(dir, 0);\n> +    commit_pointer(revision)->root_dir_offset = dir_offset(dir);\n> +    strncpy(repo_path_buffer, path, REPO_MAX_PATH_LEN);\n> +    repo_path_buffer[REPO_MAX_PATH_LEN - 1] = '\\0';\n> +    path = repo_path_buffer;\n> +    for (name = pool_tok_r(path, \"/\", &ctx); ~name;\n> +         name = pool_tok_r(NULL, \"/\", &ctx)) {\n> +        parent_dir_o = dir_offset(dir);\n> +        dirent = repo_dirent_by_name(dir, name);\n> +        if (dirent == NULL) {\n\nstatic repo_dir_t *add_subdir(uint32_t parent_dir_o, repo_dir_t *dir,\n                              uint32_t name)\n{\n\n> +            dir = repo_clone_dir(dir, 1);\n> +            dirent = &repo_first_dirent(dir)[dir->size - 1];\n> +            dirent->name_offset = name;\n> +            dirent->mode = REPO_MODE_DIR;\n> +            qsort(repo_first_dirent(dir), dir->size,\n> +                  sizeof(repo_dirent_t), repo_dirent_name_cmp);\n> +            dirent = repo_dirent_by_name(dir, name);\n> +            dir_o = dir_with_dirents_alloc(0);\n> +            dirent->content_offset = dir_o;\n> +            dir = dir_pointer(dir_o);\n\n}\n\n> +        } else if ((dir = repo_dir_from_dirent(dirent))) {\n> +            dirent_o = dirent_offset(dirent);\n> +            dir = repo_clone_dir(dir, 0);\n> +            if (dirent_o != ~0)\n> +                dirent_pointer(dirent_o)->content_offset = dir_offset(dir);\n> +        } else {\n\nstatic repo_dir_t *replace_with_empty_dir(uint32_t dirent)\n{\n\n> +            dirent->mode = REPO_MODE_DIR;\n> +            dirent_o = dirent_offset(dirent);\n> +            dir_o = dir_with_dirents_alloc(0);\n> +            dirent = dirent_pointer(dirent_o);\n> +            dir = dir_pointer(dir_o);\n> +            dirent->content_offset = dir_o;\n\n}\n\n> +        }\n> +    }\n> +    if (dirent) {\n\nWhen would it be NULL?  Is an empty path allowed?\n\nstatic void fill_dirent(repo_dirent_t *dirent,\n                        uint32_t mode, uint32_t content_offset,\n                        uint32_t parent_dir_o, int del)\n{\n\n> +        dirent->mode = mode;\n> +        dirent->content_offset = content_offset;\n> +        if (del && ~parent_dir_o) {\n> +            dirent->name_offset = ~0;\n> +            dir = dir_pointer(parent_dir_o);\n> +            qsort(repo_first_dirent(dir), dir->size,\n> +                  sizeof(repo_dirent_t), repo_dirent_name_cmp);\n> +            dir->size--;\n> +        }\n\n}\n\n> +    }\n> +}\n[...]\n\n> +static void\n> +repo_git_add_r(uint32_t depth, uint32_t *path, repo_dir_t *dir);\n> +\n> +static void\n> +repo_git_add(uint32_t depth, uint32_t *path, repo_dirent_t *dirent)\n> +{\n> +    if (repo_dirent_is_dir(dirent)) {\n> +        repo_git_add_r(depth, path, repo_dir_from_dirent(dirent));\n> +    } else {\n> +        fast_export_modify(depth, path, dirent->mode, dirent->content_offset);\n> +    }\n> +}\n\nJust curious: does gcc notice the tail calls?\n\n> +static void\n> +repo_git_add_r(uint32_t depth, uint32_t *path, repo_dir_t *dir)\n> +{\n> +    uint32_t o;\n> +    repo_dirent_t *de;\n> +    de = repo_first_dirent(dir);\n> +    for (o = 0; o < dir->size; o++) {\n> +        path[depth] = de[o].name_offset;\n> +        repo_git_add(depth + 1, path, &de[o]);\n> +    }\n> +}\n\nCan this overflow the path stack?\n\n> +\n> +static void\n> +repo_diff_r(uint32_t depth, uint32_t *path, repo_dir_t *dir1,\n> +            repo_dir_t *dir2)\n> +{\n> +    repo_dirent_t *de1, *de2, *max_de1, *max_de2;\n> +    de1 = repo_first_dirent(dir1);\n> +    de2 = repo_first_dirent(dir2);\n> +    max_de1 = &de1[dir1->size];\n> +    max_de2 = &de2[dir2->size];\n> +\n> +    while (de1 < max_de1 && de2 < max_de2) {\n> +        if (de1->name_offset < de2->name_offset) {\n> +            path[depth] = (de1++)->name_offset;\n> +            fast_export_delete(depth + 1, path);\n> +        } else if (de1->name_offset == de2->name_offset) {\n> +            path[depth] = de1->name_offset;\n> +            if (de1->content_offset != de2->content_offset) {\n> +                if (repo_dirent_is_dir(de1) && repo_dirent_is_dir(de2)) {\n> +                    repo_diff_r(depth + 1, path,\n> +                                repo_dir_from_dirent(de1),\n> +                                repo_dir_from_dirent(de2));\n> +                } else {\n> +                    if (repo_dirent_is_dir(de1) != repo_dirent_is_dir(de2)) {\n> +                        fast_export_delete(depth + 1, path);\n> +                    }\n> +                    repo_git_add(depth + 1, path, de2);\n> +                }\n> +            }\n> +            de1++;\n> +            de2++;\n> +        } else {\n> +            path[depth] = de2->name_offset;\n> +            repo_git_add(depth + 1, path, de2++);\n> +        }\n\nMight be easier to read the other way around:\n\n\t\tif (de1->name_offset < de2->name_offset) {\n\t\t\tpath[depth] = (de1++)->name_offset;\n\t\t\tfast_export_delete(depth + 1, path);\n\t\t\tcontinue;\n\t\t}\n\t\tif (de1->name_offset > de2->name_offset) {\n\t\t\tpath[depth] = de2->name_offset;\n\t\t\trepo_git_add(depth + 1, path, de2++);\n\t\t\tcontinue;\n\t\t}\n\t\t... matching paths case ...\n\n> +    }\n> +    while (de1 < max_de1) {\n> +        path[depth] = (de1++)->name_offset;\n> +        fast_export_delete(depth + 1, path);\n> +    }\n> +    while (de2 < max_de2) {\n> +        path[depth] = de2->name_offset;\n> +        repo_git_add(depth + 1, path, de2++);\n> +    }\n> +}\n[...]\n\n> +void repo_commit(uint32_t revision, char *author, char *log, char *uuid,\n> +                 char *url, time_t timestamp)\n> +{\n> +    if (revision == 0) {\n> +        active_commit = commit_alloc(1);\n> +        commit_pointer(active_commit)->root_dir_offset =\n> +            dir_with_dirents_alloc(0);\n> +    } else {\n> +        fast_export_commit(revision, author, log, uuid, url, timestamp);\n> +    }\n> +    num_dirs_saved = dir_pool.size;\n> +    num_dirents_saved = dirent_pool.size;\n\nI did not carefully trace the cases where repo_clone_dir may reuse a\ndir.  I would be happier if someone else does.\n\nThe rest looked good to me, for what it’s worth.\n\nThanks,\nJonathan\n"},{"id":"142558","messageId":"20100530085937.GA5513@progeny.tock","threadId":"23884","inReplyTo":"1274650832-7411-8-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 7/7] Add handler for SVN dump","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-30T08:59:37Z","receivedAt":"2010-05-30T08:59:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n\n> Expose an API to parse an SVN dump created with `svnadmin dump` and\n> emit a fast-import stream using fast_export and repo_tree. This is a\n> temporary workaround: it will be replaced with a client that\n> communicates live with a remote SVN server in future.\n\nHmm.  I suspect the svndump-to-fastimport converter might be useful as\na sort of swiss army knife even after remote-svn moves on to use\nthe ra lib.  What would you think about putting a main() function to\nbuild the standalone program under contrib/fast-import?\n\nI can write a patch for it for next round if you like the idea.\n\nJonathan\n"},{"id":"142559","messageId":"AANLkTilHgXD1mzrTE0PQKeLWM7uheNmZ62ZJGGxuh-7E@mail.gmail.com","threadId":"23884","inReplyTo":"20100529071811.GA6648@progeny.tock","subject":"Re: [PATCH 2/7] Add cpp macro implementation of treaps","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T09:09:17Z","receivedAt":"2010-05-30T09:09:17Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder <jrnieder@gmail.com> wrote:\n>  From: Jason Evans <jasone@canonware.com>\n>\n>  Treaps provide a memory-efficient binary search tree structure.\n>  Insertion/deletion/search are about as about as fast in the average\n>  case as red-black trees and the chances of worst-case behavior are\n>  vanishingly small, thanks to (pseudo-)randomness.  That is a small\n>  price to pay, given that treaps are much simpler to implement.\n>\n>  [db: Altered to reference nodes by offset from a common base pointer]\n>  [db: Bob Jenkins' hashing implementation dropped for Knuth's]\n>  [db: Methods unnecessary for search and insert dropped]\n>\n>  Signed-off-by: David Barr <david.barr@cordelta.com>\n>  Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n\nThanks. This can go into the commit message when I re-send the series.\n\n> I don’t know if style nitpicking is welcome here, given that the\n> code comes from elsewhere.  Should we be trying to keep the code\n> close to Jason’s version (and sending patches upstream as needed),\n> or is that not worth the trouble?\n\nIn my opinion, style nitpicks are welcome: it should conform to the\ngit.git style. I'm anyway maintaining a separate branch of master just\nfor changes specific to the git.git merge- Jason and David are welcome\nto pull patches from there.\n\n> #include \"../git-compat-util.h\" would be more portable.\n\nRight, this should be one major change in all the files in the git-merge branch.\n\n> These are defined in obj_pool.h?  Maybe this would be easier to\n> understand if the patch to add that file came sooner in the series.\n\nOkay. Note that in the revision history, trp.h was first imported, and\nthen culled to use obj_pool.h by David.\n\n> Where does this magic number come from?  (I assume Knuth, but it\n> would be nice to include a reference or explanation.)\n\nIndeed. 2654435761 is the golden ratio number corresponding with 2^32.\nSee http://bit.ly/cwAfD4\n\n> Neat.  Maybe this description should go in a file in\n> Documentation/technical, to make trp.h itself a little less daunting.\n\nOkay, I'll put it in a separate file then.\n\n> Also: http://www.canonware.com/trp/ seems to provide a test program;\n> do you think it would make sense to include it as well?\n\nProbably in documentation/technical?\n\n> [1] http://t-t-travails.blogspot.com/2008/07/treaps-versus-red-black-trees.html\n\nThis is a good read, thanks.\n\n-- Ram\n"},{"id":"142560","messageId":"AANLkTiltqraEFzYohs_89VbJQsevQC3Yp5RYWSe4b3jd@mail.gmail.com","threadId":"23884","inReplyTo":"20100529090609.GA7247@progeny.tock","subject":"Re: [PATCH 4/7] Add a memory pool library","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T09:12:40Z","receivedAt":"2010-05-30T09:12:40Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder <jrnieder@gmail.com> wrote:\n> Hmm, like talloc[1].  How do they compare?\n\nI've never used talloc. What basis can we use for the comparison?\n\n> This makes functions with names like string_alloc(), which would then\n> be un-greppable.  It might be nice (though certainly not necessary) to\n> provide an overview in Documentation/technical/ so there is no need to\n> find the definition of each function.\n\nDefinitely. On a related note, note that while compiling, a lot of\nunused functions are generated which pop up as compiler warnings- I\nhaven't figured out how to suppress them yet, and I suspect Junio\nwon't be happy with them either.\n\n-- Ram\n"},{"id":"142561","messageId":"20100530093151.GB5513@progeny.tock","threadId":"23884","inReplyTo":"AANLkTilHgXD1mzrTE0PQKeLWM7uheNmZ62ZJGGxuh-7E@mail.gmail.com","subject":"Re: [PATCH 2/7] Add cpp macro implementation of treaps","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-30T09:31:51Z","receivedAt":"2010-05-30T09:31:51Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder <jrnieder@gmail.com> wrote:\n\n>> Also: http://www.canonware.com/trp/ seems to provide a test program;\n>> do you think it would make sense to include it as well?\n>\n> Probably in documentation/technical?\n\nI was thinking in the toplevel, but I wasn’t thinking about how much\nof trp.h was removed.\n\nOf course svn-fe itself is a pretty good test that the treap is\nworking as it should.  So I wouldn’t worry about it.\n\nJonathan\n\n Makefile               |    1 +\n t/t0070-fundamental.sh |    4 ++++\n test-treap.c           |    8 ++++++++\n 3 files changed, 13 insertions(+), 0 deletions(-)\n create mode 100644 test-treap.c\n\ndiff --git a/Makefile b/Makefile\nindex 2f5d631..a26bceb 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -403,6 +403,7 @@ TEST_PROGRAMS_NEED_X += test-path-utils\n TEST_PROGRAMS_NEED_X += test-run-command\n TEST_PROGRAMS_NEED_X += test-sha1\n TEST_PROGRAMS_NEED_X += test-sigchain\n+TEST_PROGRAMS_NEED_X += test-treap\n TEST_PROGRAMS_NEED_X += test-index-version\n \n TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\ndiff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh\nindex 680d7d6..7522bdc 100755\n--- a/t/t0070-fundamental.sh\n+++ b/t/t0070-fundamental.sh\n@@ -12,4 +12,8 @@ test_expect_success 'character classes (isspace, isalpha etc.)' '\n \ttest-ctype\n '\n \n+test_expect_success 'treap data structure' '\n+\ttest-treap\n+'\n+\n test_done\ndiff --git a/test-treap.c b/test-treap.c\nnew file mode 100644\nindex 0000000..f94a931\n--- /dev/null\n+++ b/test-treap.c\n@@ -0,0 +1,8 @@\n+#include \"git-compat-util.h\"\n+#include \"vcs-svn/trp.h\"\n+#include \"vcs-svn/obj_pool.h\"\n+\n+#define NNODES 32\n+#define NSETS 200\n+\n+tests go here\n-- \n1.7.1\n"},{"id":"142562","messageId":"AANLkTikrLDwwOg3CmjVGqILW8oH1-sY19WOnkUey1RaB@mail.gmail.com","threadId":"23884","inReplyTo":"20100530093151.GB5513@progeny.tock","subject":"Re: [PATCH 2/7] Add cpp macro implementation of treaps","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T09:33:37Z","receivedAt":"2010-05-30T09:33:37Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder <jrnieder@gmail.com> wrote:\n>  Makefile               |    1 +\n>  t/t0070-fundamental.sh |    4 ++++\n>  test-treap.c           |    8 ++++++++\n>  3 files changed, 13 insertions(+), 0 deletions(-)\n>  create mode 100644 test-treap.c\n>\n> diff --git a/Makefile b/Makefile\n> index 2f5d631..a26bceb 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -403,6 +403,7 @@ TEST_PROGRAMS_NEED_X += test-path-utils\n>  TEST_PROGRAMS_NEED_X += test-run-command\n>  TEST_PROGRAMS_NEED_X += test-sha1\n>  TEST_PROGRAMS_NEED_X += test-sigchain\n> +TEST_PROGRAMS_NEED_X += test-treap\n>  TEST_PROGRAMS_NEED_X += test-index-version\n>\n>  TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))\n> diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh\n> index 680d7d6..7522bdc 100755\n> --- a/t/t0070-fundamental.sh\n> +++ b/t/t0070-fundamental.sh\n> @@ -12,4 +12,8 @@ test_expect_success 'character classes (isspace, isalpha etc.)' '\n>        test-ctype\n>  '\n>\n> +test_expect_success 'treap data structure' '\n> +       test-treap\n> +'\n> +\n>  test_done\n> diff --git a/test-treap.c b/test-treap.c\n> new file mode 100644\n> index 0000000..f94a931\n> --- /dev/null\n> +++ b/test-treap.c\n> @@ -0,0 +1,8 @@\n> +#include \"git-compat-util.h\"\n> +#include \"vcs-svn/trp.h\"\n> +#include \"vcs-svn/obj_pool.h\"\n> +\n> +#define NNODES 32\n> +#define NSETS 200\n> +\n> +tests go here\n> --\n> 1.7.1\n\nThanks for the template. I'll start writing tests right away.\n\n-- Ram\n"},{"id":"142563","messageId":"AANLkTil6dK82ydjjBqKlHEuDMHbgOlVAUXNQZGHZzCYM@mail.gmail.com","threadId":"23884","inReplyTo":"20100529113800.GA7925@progeny.tock","subject":"Re: [PATCH 5/7] Add API for string-specific memory pool","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T09:38:33Z","receivedAt":"2010-05-30T09:38:33Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder wrote:\n> This interns strings so they can be compared by address.  Interesting.\n> The use of offsets instead of pointers here mean it is possible to\n> back the pool by a file, if I understand correctly.\n\nExactly. The future plan is to use file backing to support incremental dumps.\n\n> Missing From: and sign-off.\n\nRight, I'll fix this in all the patches in the series in the re-send.\n\n> nitpick: could use fewer parentheses.\n\nOk.\n\n> so I guess it is safer to keep the casts.\n\nRight. I don't want compiler warnings either- Sverre and I had to work\nquite hard to eliminate many of them (commit seen in master now).\n\n> style nitpick: should use space instead of tab\n\nQuick doubt: Does running whitespace-cleanup (Emacs) on all the files\nbefore generating patches suffice?\n\n> Should use \"../git-compat-util.h\" for portability: unfortunately, some\n> platforms git supports still don’t have stdint.h iirc.\n\nOh. What do we do about stdint.h then?\n\n-- Ram\n"},{"id":"142564","messageId":"20100530095537.GA6242@progeny.tock","threadId":"23884","inReplyTo":"AANLkTiltqraEFzYohs_89VbJQsevQC3Yp5RYWSe4b3jd@mail.gmail.com","subject":"Re: [PATCH 4/7] Add a memory pool library","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-30T09:55:37Z","receivedAt":"2010-05-30T09:55:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Ram,\n\nRamkumar Ramachandra wrote:\n\n> I've never used talloc. What basis can we use for the comparison?\n\nAh, a little explanation is in order.\n\nYou see, I read the commit message, having just looked at the treap\nimplementation, read the word “allocator”, and decided that a\nmini-malloc was exactly the sort of thing I did not want to read right\nthen.  Hence the silly comment about some allocator that pools memory\nfor heterogeneous-sized blocks (which is very nice and convenient, but\nnot so relevant!).  Sorry --- I should not have been so lazy.\n\nIn fact, this patch just maintains a growing array of never-freed\nfixed-size blocks.  It is very similar to the existing alloc.c but\nwith some small differences:\n\n . uses a single memory area that grows with realloc, so it can be\n   freed for valgrind-cleanness if one wants.\n\n . grows a little more aggressively, as David pointed out\n\nAnd it is much simpler than I was thinking, so honestly, I don’t\neven mind the code duplication so much.\n\nFor interest, the analogous CCAN module is\nhttp://ccan.ozlabs.org/info/block_pool.html\n\n> On a related note, note that while compiling, a lot of\n> unused functions are generated which pop up as compiler warnings\n\nMaybe __attribute__((__unused__)) could work.\n\nSorry for the nonsense,\nJonathan\n"},{"id":"142565","messageId":"20100530100929.GB6242@progeny.tock","threadId":"23884","inReplyTo":"AANLkTil6dK82ydjjBqKlHEuDMHbgOlVAUXNQZGHZzCYM@mail.gmail.com","subject":"Re: [PATCH 5/7] Add API for string-specific memory pool","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-05-30T10:09:30Z","receivedAt":"2010-05-30T10:09:30Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Jonathan Nieder wrote:\n\n>> style nitpick: should use space instead of tab\n> \n> Quick doubt: Does running whitespace-cleanup (Emacs) on all the files\n> before generating patches suffice?\n\nNo idea.  Documentation/CodingStyle in linux-2.6 has some emacs tips.\n\n>> some platforms git supports still don’t have stdint.h iirc.\n>\n> Oh. What do we do about stdint.h then?\n\nLuckily, the systems without stdint.h have inttypes.h (and vice\nversa[1]), so git-compat-util.h takes care of it.\n\nJonathan\n\n[1] inttypes.h is the older standard, and almost all systems support\nit, but Interix has stdint.h but not inttypes.h.  So it goes.\n"},{"id":"142566","messageId":"AANLkTimFQAzmiRHySSSVbnfGb8WMNCF_3z9vBOCdOdiV@mail.gmail.com","threadId":"23884","inReplyTo":"20100530085937.GA5513@progeny.tock","subject":"Re: [PATCH 7/7] Add handler for SVN dump","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T10:45:08Z","receivedAt":"2010-05-30T10:45:08Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nJonathan Nieder wrote:\n> Hmm.  I suspect the svndump-to-fastimport converter might be useful as\n> a sort of swiss army knife even after remote-svn moves on to use\n> the ra lib.  What would you think about putting a main() function to\n> build the standalone program under contrib/fast-import?\n\nYes, I like the idea. I'll just sort out a few things in my GitHub\nfork and tell you when something's ready.\n\n> I can write a patch for it for next round if you like the idea.\n\nUm, I'd suggest waiting a little bit- my fork's in a bit of a mess\nwith all these changes from your reviews, and I don't want to merge in\nmore changes yet :p\n\n-- Ram\n"},{"id":"142567","messageId":"AANLkTimVu-8MpJjT8Y_dU7V31JxstPPgbV16TyaXSk7W@mail.gmail.com","threadId":"23884","inReplyTo":"20100530095537.GA6242@progeny.tock","subject":"Re: [PATCH 4/7] Add a memory pool library","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T10:51:19Z","receivedAt":"2010-05-30T10:51:19Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder wrote:\n> And it is much simpler than I was thinking, so honestly, I don’t\n> even mind the code duplication so much.\n\nOk, we can keep this code then.\n\n> Maybe __attribute__((__unused__)) could work.\n\nI thought of exactly that, but it needs to be used at declaration time\nwhich doesn't seem to be possible since the function is declared by\ncpp macros.\n\n-- Ram\n"},{"id":"142576","messageId":"AANLkTimPkAWa8YEro50gWgeaPSmgPCRXppHZNZuCb_hj@mail.gmail.com","threadId":"23884","inReplyTo":"20100529140645.GA8338@progeny.tock","subject":"Re: [PATCH 6/7] Add SVN revision parser and exporter","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T15:58:56Z","receivedAt":"2010-05-30T15:58:56Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Jonathan,\n\nJonathan Nieder wrote:\n> Wait, where are SVN revisions being parsed?  It seems that the repo\n> module maintains the exporter's state and provides a facility to\n> to call the fast_export module to write it out.\n\nRight. Sorry about the bad commit message.\n\n>  repo_add(path, mode, blob_mark) is used to add a new file to\n>  the current commit.\n>\n>  repo_modify is used to add a replacement for an existing file;\n>  it is implemented exactly the same way, but a check could be\n>  added later to distinguish the two cases.\n>\n>  repo_copy copies a blob from a previous revision to the current\n>  commit.\n>\n>  repo_replace modifies the content of a file from the current\n>  commit, if and only if it already exists.\n>\n>  repo_delete removes a file or directory from the current commit.\n>\n>  repo_commit calls out to fast_export to write the current commit\n>  to the fast-import stream in stdout.\n>\n>  repo_diff is used by the fast_export module to write the changes\n>  for a commit.\n>\n>  repo_reset erases the exporter's state, so valgrind can be happy.\n\nThanks for the nice descriptions. Will be useful while preparing\nDocumentation/technical.\n\n> Mode must be 100644, 100755, 120000, or 160000.\n\nOkay, do we put in an assert() for sanity?\n\n> Style: we don’t tend to use typedef to hide underlying struct definitions\n> (see Documentation/CodingStyle from linux-2.6.git, chapter 5, for some\n> explanation about why).\n\nFixed.\n\n> Maybe some local variables would make this more readable:\n\nFixed.\n\n> Is this for adding new entries to an existing directory?  It is\n> getting late, so I did not look it over carefully.\n\nFor David.\n\n> If a file \"foo\" exists and I ask for \"foo/bar\", this will return\n> the entry for foo.  Is that appropriate?\n\nFor David.\n\n> When would it be NULL?  Is an empty path allowed?\n\nFor David.\n\n> Can this overflow the path stack?\n\nActually, this has already been changed- see the latest commits to see\nhow path is allocated.\n\n> Might be easier to read the other way around:\n\nFixed.\n\n> I did not carefully trace the cases where repo_clone_dir may reuse a\n> dir.  I would be happier if someone else does.\n\nFor David.\n\n-- Ram\n"},{"id":"142578","messageId":"AANLkTikiuFeGkDizLTmB_3AQgiU9vL3C_Tgxg_lf4LgL@mail.gmail.com","threadId":"23884","inReplyTo":"20100529113800.GA7925@progeny.tock","subject":"Re: [PATCH 5/7] Add API for string-specific memory pool","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2010-05-30T16:52:55Z","receivedAt":"2010-05-30T16:52:55Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi again,\n\nJonathan Nieder wrote:\n>> +static int node_indentity_cmp(node_t *a, node_t *b)\n>> +{\n>> +    int r = node_value_cmp(a, b);\n>> +    return r ? r : (((uintptr_t) a) > ((uintptr_t) b))\n>> +        - (((uintptr_t) a) < ((uintptr_t) b));\n>\n> nitpick: could use fewer parentheses.\n>\n>        return r ? r : (((uintptr_t) a > (uintptr_t) b) - ...\n\nActually, this triggers a compiler error on GCC:\nstring_pool.c:34: warning: comparisons like ‘X<=Y<=Z’ do not have\ntheir mathematical meaning\n\n-- Ram\n"}]}