mercurial/dirs.c
branchstable
changeset 24803 e89f909edffa
parent 24651 67241ee427cf
child 25015 b3a68fb8b859
equal deleted inserted replaced
24753:612ed41ae359 24803:e89f909edffa
     7  the GNU General Public License, incorporated herein by reference.
     7  the GNU General Public License, incorporated herein by reference.
     8 */
     8 */
     9 
     9 
    10 #define PY_SSIZE_T_CLEAN
    10 #define PY_SSIZE_T_CLEAN
    11 #include <Python.h>
    11 #include <Python.h>
       
    12 #include <string.h>
    12 #include "util.h"
    13 #include "util.h"
    13 
    14 
    14 /*
    15 /*
    15  * This is a multiset of directory names, built from the files that
    16  * This is a multiset of directory names, built from the files that
    16  * appear in a dirstate or manifest.
    17  * appear in a dirstate or manifest.
    30 
    31 
    31 static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos)
    32 static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos)
    32 {
    33 {
    33 	const char *s = PyString_AS_STRING(path);
    34 	const char *s = PyString_AS_STRING(path);
    34 
    35 
    35 	while (pos != -1) {
    36 	const char *ret = strchr(s + pos, '/');
    36 		if (s[pos] == '/')
    37 	return (ret != NULL) ? (ret - s) : -1;
    37 			break;
       
    38 		pos -= 1;
       
    39 	}
       
    40 
       
    41 	return pos;
       
    42 }
    38 }
    43 
    39 
    44 static int _addpath(PyObject *dirs, PyObject *path)
    40 static int _addpath(PyObject *dirs, PyObject *path)
    45 {
    41 {
    46 	const char *cpath = PyString_AS_STRING(path);
    42 	char *cpath = PyString_AS_STRING(path);
    47 	Py_ssize_t pos = PyString_GET_SIZE(path);
    43 	Py_ssize_t len = PyString_GET_SIZE(path);
       
    44 	Py_ssize_t pos = -1;
    48 	PyObject *key = NULL;
    45 	PyObject *key = NULL;
    49 	int ret = -1;
    46 	int ret = -1;
    50 
    47 
    51 	while ((pos = _finddir(path, pos - 1)) != -1) {
    48 	while ((pos = _finddir(path, pos + 1)) != -1) {
    52 		PyObject *val;
    49 		PyObject *val;
    53 
    50 
    54 		/* It's likely that every prefix already has an entry
    51 		/* It's likely that every prefix already has an entry
    55 		   in our dict. Try to avoid allocating and
    52 		   in our dict. Try to avoid allocating and
    56 		   deallocating a string for each prefix we check. */
    53 		   deallocating a string for each prefix we check. */
    57 		if (key != NULL)
    54 		if (key != NULL)
    58 			((PyStringObject *)key)->ob_shash = -1;
    55 			((PyStringObject *)key)->ob_shash = -1;
    59 		else {
    56 		else if (pos != 0) {
    60 			/* Force Python to not reuse a small shared string. */
    57 			/* pos >= 1, which means that len >= 2. This is
    61 			key = PyString_FromStringAndSize(cpath,
    58 			   guaranteed to produce a non-interned string. */
    62 							 pos < 2 ? 2 : pos);
    59 			key = PyString_FromStringAndSize(cpath, len);
       
    60 			if (key == NULL)
       
    61 				goto bail;
       
    62 		} else {
       
    63 			/* pos == 0, which means we need to increment the dir
       
    64 			   count for the empty string. We need to make sure we
       
    65 			   don't muck around with interned strings, so throw it
       
    66 			   away later. */
       
    67 			key = PyString_FromString("");
    63 			if (key == NULL)
    68 			if (key == NULL)
    64 				goto bail;
    69 				goto bail;
    65 		}
    70 		}
    66 		PyString_GET_SIZE(key) = pos;
    71 		PyString_GET_SIZE(key) = pos;
    67 		PyString_AS_STRING(key)[pos] = '\0';
    72 		PyString_AS_STRING(key)[pos] = '\0';
    68 
    73 
    69 		val = PyDict_GetItem(dirs, key);
    74 		val = PyDict_GetItem(dirs, key);
    70 		if (val != NULL) {
    75 		if (val != NULL) {
    71 			PyInt_AS_LONG(val) += 1;
    76 			PyInt_AS_LONG(val) += 1;
       
    77 			if (pos != 0)
       
    78 				PyString_AS_STRING(key)[pos] = '/';
       
    79 			else
       
    80 				key = NULL;
    72 			continue;
    81 			continue;
    73 		}
    82 		}
    74 
    83 
    75 		/* Force Python to not reuse a small shared int. */
    84 		/* Force Python to not reuse a small shared int. */
    76 		val = PyInt_FromLong(0x1eadbeef);
    85 		val = PyInt_FromLong(0x1eadbeef);
    81 		PyInt_AS_LONG(val) = 1;
    90 		PyInt_AS_LONG(val) = 1;
    82 		ret = PyDict_SetItem(dirs, key, val);
    91 		ret = PyDict_SetItem(dirs, key, val);
    83 		Py_DECREF(val);
    92 		Py_DECREF(val);
    84 		if (ret == -1)
    93 		if (ret == -1)
    85 			goto bail;
    94 			goto bail;
       
    95 
       
    96 		/* Clear the key out since we've already exposed it to Python
       
    97 		   and can't mutate it further. */
    86 		Py_CLEAR(key);
    98 		Py_CLEAR(key);
    87 	}
    99 	}
    88 	ret = 0;
   100 	ret = 0;
    89 
   101 
    90 bail:
   102 bail:
    93 	return ret;
   105 	return ret;
    94 }
   106 }
    95 
   107 
    96 static int _delpath(PyObject *dirs, PyObject *path)
   108 static int _delpath(PyObject *dirs, PyObject *path)
    97 {
   109 {
    98 	Py_ssize_t pos = PyString_GET_SIZE(path);
   110 	Py_ssize_t pos = -1;
    99 	PyObject *key = NULL;
   111 	PyObject *key = NULL;
   100 	int ret = -1;
   112 	int ret = -1;
   101 
   113 
   102 	while ((pos = _finddir(path, pos - 1)) != -1) {
   114 	while ((pos = _finddir(path, pos + 1)) != -1) {
   103 		PyObject *val;
   115 		PyObject *val;
   104 
   116 
   105 		key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
   117 		key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
   106 
   118 
   107 		if (key == NULL)
   119 		if (key == NULL)