mercurial/pathencode.c
changeset 17606 318fb32b980e
child 17616 9535a0dc41f2
equal deleted inserted replaced
17605:e9cc29be3305 17606:318fb32b980e
       
     1 /*
       
     2  pathencode.c - efficient path name encoding
       
     3 
       
     4  Copyright 2012 Facebook
       
     5 
       
     6  This software may be used and distributed according to the terms of
       
     7  the GNU General Public License, incorporated herein by reference.
       
     8 */
       
     9 
       
    10 #include <Python.h>
       
    11 #include <assert.h>
       
    12 #include <ctype.h>
       
    13 #include <stdlib.h>
       
    14 #include <string.h>
       
    15 
       
    16 #include "util.h"
       
    17 
       
    18 /* state machine for dir-encoding */
       
    19 enum dir_state {
       
    20 	DDOT,
       
    21 	DH,
       
    22 	DHGDI,
       
    23 	DDEFAULT,
       
    24 };
       
    25 
       
    26 static inline void charcopy(char *dest, Py_ssize_t *destlen, size_t destsize,
       
    27                             char c)
       
    28 {
       
    29 	if (dest) {
       
    30 		assert(*destlen < destsize);
       
    31 		dest[*destlen] = c;
       
    32 	}
       
    33 	(*destlen)++;
       
    34 }
       
    35 
       
    36 static inline void memcopy(char *dest, Py_ssize_t *destlen, size_t destsize,
       
    37                            const void *src, Py_ssize_t len)
       
    38 {
       
    39 	if (dest) {
       
    40 		assert(*destlen + len < destsize);
       
    41 		memcpy((void *)&dest[*destlen], src, len);
       
    42 	}
       
    43 	*destlen += len;
       
    44 }
       
    45 
       
    46 static Py_ssize_t _encodedir(char *dest, size_t destsize,
       
    47                              const char *src, Py_ssize_t len)
       
    48 {
       
    49 	enum dir_state state = DDEFAULT;
       
    50 	Py_ssize_t i = 0, destlen = 0;
       
    51 
       
    52 	while (i < len) {
       
    53 		switch (state) {
       
    54 		case DDOT:
       
    55 			switch (src[i]) {
       
    56 			case 'd':
       
    57 			case 'i':
       
    58 				state = DHGDI;
       
    59 				charcopy(dest, &destlen, destsize, src[i++]);
       
    60 				break;
       
    61 			case 'h':
       
    62 				state = DH;
       
    63 				charcopy(dest, &destlen, destsize, src[i++]);
       
    64 				break;
       
    65 			default:
       
    66 				state = DDEFAULT;
       
    67 				break;
       
    68 			}
       
    69 			break;
       
    70 		case DH:
       
    71 			if (src[i] == 'g') {
       
    72 				state = DHGDI;
       
    73 				charcopy(dest, &destlen, destsize, src[i++]);
       
    74 			}
       
    75 			else state = DDEFAULT;
       
    76 			break;
       
    77 		case DHGDI:
       
    78 			if (src[i] == '/') {
       
    79 				memcopy(dest, &destlen, destsize, ".hg", 3);
       
    80 				charcopy(dest, &destlen, destsize, src[i++]);
       
    81 			}
       
    82 			state = DDEFAULT;
       
    83 			break;
       
    84 		case DDEFAULT:
       
    85 			if (src[i] == '.')
       
    86 				state = DDOT;
       
    87 			charcopy(dest, &destlen, destsize, src[i++]);
       
    88 			break;
       
    89 		}
       
    90 	}
       
    91 
       
    92 	return destlen;
       
    93 }
       
    94 
       
    95 PyObject *encodedir(PyObject *self, PyObject *args)
       
    96 {
       
    97 	Py_ssize_t len, newlen;
       
    98 	PyObject *pathobj, *newobj;
       
    99 	char *path;
       
   100 
       
   101 	if (!PyArg_ParseTuple(args, "O:encodedir", &pathobj))
       
   102 		return NULL;
       
   103 
       
   104 	if (PyString_AsStringAndSize(pathobj, &path, &len) == -1) {
       
   105 		PyErr_SetString(PyExc_TypeError, "expected a string");
       
   106 		return NULL;
       
   107 	}
       
   108 
       
   109 	newlen = len ? _encodedir(NULL, 0, path, len + 1) : 1;
       
   110 
       
   111 	if (newlen == len + 1) {
       
   112 		Py_INCREF(pathobj);
       
   113 		return pathobj;
       
   114 	}
       
   115 
       
   116 	newobj = PyString_FromStringAndSize(NULL, newlen);
       
   117 
       
   118 	if (newobj) {
       
   119 		PyString_GET_SIZE(newobj)--;
       
   120 		_encodedir(PyString_AS_STRING(newobj), newlen, path,
       
   121 			   len + 1);
       
   122 	}
       
   123 
       
   124 	return newobj;
       
   125 }