Patchwork [5,of,6,foldmap-in-C] parsers: add a C function to create a file foldmap

login
register
mail settings
Submitter Siddharth Agarwal
Date April 3, 2015, 4:02 a.m.
Message ID <d6924eb0182c49f91631.1428033738@devbig136.prn2.facebook.com>
Download mbox | patch
Permalink /patch/8474/
State Accepted
Commit 670aaee7931cdff354af49371428ac371d241acb
Headers show

Comments

Siddharth Agarwal - April 3, 2015, 4:02 a.m.
# HG changeset patch
# User Siddharth Agarwal <sid0@fb.com>
# Date 1427869947 25200
#      Tue Mar 31 23:32:27 2015 -0700
# Node ID d6924eb0182c49f916315794f905ed90852a6205
# Parent  db7e6229985f344c66c3f079d8d86cd9bb1054ba
parsers: add a C function to create a file foldmap

This is a hot path on case-insensitive filesystems -- it's guaranteed to be
called every time 'hg status' is run.

This is significantly faster than the equivalent Python code: see the following
patch for numbers.

Patch

diff --git a/mercurial/parsers.c b/mercurial/parsers.c
--- a/mercurial/parsers.c
+++ b/mercurial/parsers.c
@@ -173,6 +173,72 @@  static PyObject *asciiupper(PyObject *se
 	return _asciitransform(str_obj, uppertable, NULL);
 }
 
+static PyObject *make_file_foldmap(PyObject *self, PyObject *args)
+{
+	PyObject *dmap, *spec_obj, *normcase_fallback;
+	PyObject *file_foldmap = NULL;
+	enum normcase_spec spec;
+	PyObject *k, *v;
+	dirstateTupleObject *tuple;
+	Py_ssize_t pos = 0;
+	const char *table;
+
+	if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
+			      &PyDict_Type, &dmap,
+			      &PyInt_Type, &spec_obj,
+			      &PyFunction_Type, &normcase_fallback))
+		goto quit;
+
+	spec = PyInt_AS_LONG(spec_obj);
+	switch (spec) {
+	case NORMCASE_LOWER:
+		table = lowertable;
+		break;
+	case NORMCASE_UPPER:
+		table = uppertable;
+		break;
+	case NORMCASE_OTHER:
+		table = NULL;
+		break;
+	default:
+		PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
+		goto quit;
+	}
+
+	file_foldmap = PyDict_New();
+	if (file_foldmap == NULL)
+		goto quit;
+
+	while (PyDict_Next(dmap, &pos, &k, &v)) {
+		if (!dirstate_tuple_check(v)) {
+			PyErr_SetString(PyExc_TypeError,
+					"expected a dirstate tuple");
+			goto quit;
+		}
+
+		tuple = (dirstateTupleObject *)v;
+		if (tuple->state != 'r') {
+			PyObject *normed;
+			if (table != NULL) {
+				normed = _asciitransform(k, table,
+					normcase_fallback);
+			} else {
+				normed = PyObject_CallFunctionObjArgs(
+					normcase_fallback, k, NULL);
+			}
+
+			if (normed == NULL)
+				goto quit;
+			if (PyDict_SetItem(file_foldmap, normed, k) == -1)
+				goto quit;
+		}
+	}
+	return file_foldmap;
+quit:
+	Py_XDECREF(file_foldmap);
+	return NULL;
+}
+
 /*
  * This code assumes that a manifest is stitched together with newline
  * ('\n') characters.
@@ -2463,6 +2529,8 @@  static PyMethodDef methods[] = {
 	{"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
 	{"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
 	{"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
+	{"make_file_foldmap", make_file_foldmap, METH_VARARGS,
+	 "make file foldmap\n"},
 	{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
 	{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
 	{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},