##// END OF EJS Templates
dirstate-item: move the C implementation to the same logic...
marmoute -
r48760:83f0e93e default
parent child Browse files
Show More
@@ -1,413 +1,413 b''
1 /*
1 /*
2 charencode.c - miscellaneous character encoding
2 charencode.c - miscellaneous character encoding
3
3
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
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 <assert.h>
12 #include <assert.h>
13
13
14 #include "charencode.h"
14 #include "charencode.h"
15 #include "compat.h"
15 #include "compat.h"
16 #include "util.h"
16 #include "util.h"
17
17
18 #ifdef IS_PY3K
18 #ifdef IS_PY3K
19 /* The mapping of Python types is meant to be temporary to get Python
19 /* The mapping of Python types is meant to be temporary to get Python
20 * 3 to compile. We should remove this once Python 3 support is fully
20 * 3 to compile. We should remove this once Python 3 support is fully
21 * supported and proper types are used in the extensions themselves. */
21 * supported and proper types are used in the extensions themselves. */
22 #define PyInt_Type PyLong_Type
22 #define PyInt_Type PyLong_Type
23 #define PyInt_AS_LONG PyLong_AS_LONG
23 #define PyInt_AS_LONG PyLong_AS_LONG
24 #endif
24 #endif
25
25
26 /* clang-format off */
26 /* clang-format off */
27 static const char lowertable[128] = {
27 static const char lowertable[128] = {
28 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
28 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
29 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
29 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
30 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
30 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
31 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
31 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
32 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
32 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
33 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
33 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
34 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
34 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
35 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
35 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
36 '\x40',
36 '\x40',
37 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
37 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
38 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
38 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
39 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
39 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
40 '\x78', '\x79', '\x7a', /* X-Z */
40 '\x78', '\x79', '\x7a', /* X-Z */
41 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
41 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
42 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
42 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
43 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
43 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
44 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
44 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
45 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
45 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
46 };
46 };
47
47
48 static const char uppertable[128] = {
48 static const char uppertable[128] = {
49 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
49 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
50 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
50 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
51 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
51 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
52 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
52 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
53 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
53 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
54 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
54 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
55 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
55 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
56 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
56 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
57 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
57 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
58 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
58 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
59 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
59 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
60 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
60 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
61 '\x60',
61 '\x60',
62 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
62 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
63 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
63 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
64 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
64 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
65 '\x58', '\x59', '\x5a', /* x-z */
65 '\x58', '\x59', '\x5a', /* x-z */
66 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
66 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
67 };
67 };
68
68
69 /* 1: no escape, 2: \<c>, 6: \u<x> */
69 /* 1: no escape, 2: \<c>, 6: \u<x> */
70 static const uint8_t jsonlentable[256] = {
70 static const uint8_t jsonlentable[256] = {
71 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 6, 2, 2, 6, 6, /* b, t, n, f, r */
71 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 6, 2, 2, 6, 6, /* b, t, n, f, r */
72 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
72 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
73 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* " */
73 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* " */
74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
75 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
75 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
76 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, /* \\ */
76 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, /* \\ */
77 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
77 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
78 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, /* DEL */
78 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, /* DEL */
79 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
79 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
80 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
80 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
81 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
81 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
82 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
82 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
83 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
83 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
84 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
84 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
85 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
85 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
86 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
86 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
87 };
87 };
88
88
89 static const uint8_t jsonparanoidlentable[128] = {
89 static const uint8_t jsonparanoidlentable[128] = {
90 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 6, 2, 2, 6, 6, /* b, t, n, f, r */
90 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 6, 2, 2, 6, 6, /* b, t, n, f, r */
91 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
91 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
92 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* " */
92 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* " */
93 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 6, 1, /* <, > */
93 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 6, 1, /* <, > */
94 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
94 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
95 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, /* \\ */
95 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, /* \\ */
96 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
96 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
97 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, /* DEL */
97 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, /* DEL */
98 };
98 };
99
99
100 static const char hexchartable[16] = {
100 static const char hexchartable[16] = {
101 '0', '1', '2', '3', '4', '5', '6', '7',
101 '0', '1', '2', '3', '4', '5', '6', '7',
102 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
102 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
103 };
103 };
104 /* clang-format on */
104 /* clang-format on */
105
105
106 /*
106 /*
107 * Turn a hex-encoded string into binary.
107 * Turn a hex-encoded string into binary.
108 */
108 */
109 PyObject *unhexlify(const char *str, Py_ssize_t len)
109 PyObject *unhexlify(const char *str, Py_ssize_t len)
110 {
110 {
111 PyObject *ret;
111 PyObject *ret;
112 char *d;
112 char *d;
113 Py_ssize_t i;
113 Py_ssize_t i;
114
114
115 ret = PyBytes_FromStringAndSize(NULL, len / 2);
115 ret = PyBytes_FromStringAndSize(NULL, len / 2);
116
116
117 if (!ret) {
117 if (!ret) {
118 return NULL;
118 return NULL;
119 }
119 }
120
120
121 d = PyBytes_AsString(ret);
121 d = PyBytes_AsString(ret);
122
122
123 for (i = 0; i < len;) {
123 for (i = 0; i < len;) {
124 int hi = hexdigit(str, i++);
124 int hi = hexdigit(str, i++);
125 int lo = hexdigit(str, i++);
125 int lo = hexdigit(str, i++);
126 *d++ = (hi << 4) | lo;
126 *d++ = (hi << 4) | lo;
127 }
127 }
128
128
129 return ret;
129 return ret;
130 }
130 }
131
131
132 PyObject *isasciistr(PyObject *self, PyObject *args)
132 PyObject *isasciistr(PyObject *self, PyObject *args)
133 {
133 {
134 const char *buf;
134 const char *buf;
135 Py_ssize_t i, len;
135 Py_ssize_t i, len;
136 if (!PyArg_ParseTuple(args, PY23("s#:isasciistr", "y#:isasciistr"),
136 if (!PyArg_ParseTuple(args, PY23("s#:isasciistr", "y#:isasciistr"),
137 &buf, &len)) {
137 &buf, &len)) {
138 return NULL;
138 return NULL;
139 }
139 }
140 i = 0;
140 i = 0;
141 /* char array in PyStringObject should be at least 4-byte aligned */
141 /* char array in PyStringObject should be at least 4-byte aligned */
142 if (((uintptr_t)buf & 3) == 0) {
142 if (((uintptr_t)buf & 3) == 0) {
143 const uint32_t *p = (const uint32_t *)buf;
143 const uint32_t *p = (const uint32_t *)buf;
144 for (; i < len / 4; i++) {
144 for (; i < len / 4; i++) {
145 if (p[i] & 0x80808080U) {
145 if (p[i] & 0x80808080U) {
146 Py_RETURN_FALSE;
146 Py_RETURN_FALSE;
147 }
147 }
148 }
148 }
149 i *= 4;
149 i *= 4;
150 }
150 }
151 for (; i < len; i++) {
151 for (; i < len; i++) {
152 if (buf[i] & 0x80) {
152 if (buf[i] & 0x80) {
153 Py_RETURN_FALSE;
153 Py_RETURN_FALSE;
154 }
154 }
155 }
155 }
156 Py_RETURN_TRUE;
156 Py_RETURN_TRUE;
157 }
157 }
158
158
159 static inline PyObject *
159 static inline PyObject *
160 _asciitransform(PyObject *str_obj, const char table[128], PyObject *fallback_fn)
160 _asciitransform(PyObject *str_obj, const char table[128], PyObject *fallback_fn)
161 {
161 {
162 char *str, *newstr;
162 char *str, *newstr;
163 Py_ssize_t i, len;
163 Py_ssize_t i, len;
164 PyObject *newobj = NULL;
164 PyObject *newobj = NULL;
165 PyObject *ret = NULL;
165 PyObject *ret = NULL;
166
166
167 str = PyBytes_AS_STRING(str_obj);
167 str = PyBytes_AS_STRING(str_obj);
168 len = PyBytes_GET_SIZE(str_obj);
168 len = PyBytes_GET_SIZE(str_obj);
169
169
170 newobj = PyBytes_FromStringAndSize(NULL, len);
170 newobj = PyBytes_FromStringAndSize(NULL, len);
171 if (!newobj) {
171 if (!newobj) {
172 goto quit;
172 goto quit;
173 }
173 }
174
174
175 newstr = PyBytes_AS_STRING(newobj);
175 newstr = PyBytes_AS_STRING(newobj);
176
176
177 for (i = 0; i < len; i++) {
177 for (i = 0; i < len; i++) {
178 char c = str[i];
178 char c = str[i];
179 if (c & 0x80) {
179 if (c & 0x80) {
180 if (fallback_fn != NULL) {
180 if (fallback_fn != NULL) {
181 ret = PyObject_CallFunctionObjArgs(
181 ret = PyObject_CallFunctionObjArgs(
182 fallback_fn, str_obj, NULL);
182 fallback_fn, str_obj, NULL);
183 } else {
183 } else {
184 PyObject *err = PyUnicodeDecodeError_Create(
184 PyObject *err = PyUnicodeDecodeError_Create(
185 "ascii", str, len, i, (i + 1),
185 "ascii", str, len, i, (i + 1),
186 "unexpected code byte");
186 "unexpected code byte");
187 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
187 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
188 Py_XDECREF(err);
188 Py_XDECREF(err);
189 }
189 }
190 goto quit;
190 goto quit;
191 }
191 }
192 newstr[i] = table[(unsigned char)c];
192 newstr[i] = table[(unsigned char)c];
193 }
193 }
194
194
195 ret = newobj;
195 ret = newobj;
196 Py_INCREF(ret);
196 Py_INCREF(ret);
197 quit:
197 quit:
198 Py_XDECREF(newobj);
198 Py_XDECREF(newobj);
199 return ret;
199 return ret;
200 }
200 }
201
201
202 PyObject *asciilower(PyObject *self, PyObject *args)
202 PyObject *asciilower(PyObject *self, PyObject *args)
203 {
203 {
204 PyObject *str_obj;
204 PyObject *str_obj;
205 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj)) {
205 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj)) {
206 return NULL;
206 return NULL;
207 }
207 }
208 return _asciitransform(str_obj, lowertable, NULL);
208 return _asciitransform(str_obj, lowertable, NULL);
209 }
209 }
210
210
211 PyObject *asciiupper(PyObject *self, PyObject *args)
211 PyObject *asciiupper(PyObject *self, PyObject *args)
212 {
212 {
213 PyObject *str_obj;
213 PyObject *str_obj;
214 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj)) {
214 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj)) {
215 return NULL;
215 return NULL;
216 }
216 }
217 return _asciitransform(str_obj, uppertable, NULL);
217 return _asciitransform(str_obj, uppertable, NULL);
218 }
218 }
219
219
220 PyObject *make_file_foldmap(PyObject *self, PyObject *args)
220 PyObject *make_file_foldmap(PyObject *self, PyObject *args)
221 {
221 {
222 PyObject *dmap, *spec_obj, *normcase_fallback;
222 PyObject *dmap, *spec_obj, *normcase_fallback;
223 PyObject *file_foldmap = NULL;
223 PyObject *file_foldmap = NULL;
224 enum normcase_spec spec;
224 enum normcase_spec spec;
225 PyObject *k, *v;
225 PyObject *k, *v;
226 dirstateItemObject *tuple;
226 dirstateItemObject *tuple;
227 Py_ssize_t pos = 0;
227 Py_ssize_t pos = 0;
228 const char *table;
228 const char *table;
229
229
230 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap", &PyDict_Type,
230 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap", &PyDict_Type,
231 &dmap, &PyInt_Type, &spec_obj, &PyFunction_Type,
231 &dmap, &PyInt_Type, &spec_obj, &PyFunction_Type,
232 &normcase_fallback)) {
232 &normcase_fallback)) {
233 goto quit;
233 goto quit;
234 }
234 }
235
235
236 spec = (int)PyInt_AS_LONG(spec_obj);
236 spec = (int)PyInt_AS_LONG(spec_obj);
237 switch (spec) {
237 switch (spec) {
238 case NORMCASE_LOWER:
238 case NORMCASE_LOWER:
239 table = lowertable;
239 table = lowertable;
240 break;
240 break;
241 case NORMCASE_UPPER:
241 case NORMCASE_UPPER:
242 table = uppertable;
242 table = uppertable;
243 break;
243 break;
244 case NORMCASE_OTHER:
244 case NORMCASE_OTHER:
245 table = NULL;
245 table = NULL;
246 break;
246 break;
247 default:
247 default:
248 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
248 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
249 goto quit;
249 goto quit;
250 }
250 }
251
251
252 /* Add some more entries to deal with additions outside this
252 /* Add some more entries to deal with additions outside this
253 function. */
253 function. */
254 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
254 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
255 if (file_foldmap == NULL) {
255 if (file_foldmap == NULL) {
256 goto quit;
256 goto quit;
257 }
257 }
258
258
259 while (PyDict_Next(dmap, &pos, &k, &v)) {
259 while (PyDict_Next(dmap, &pos, &k, &v)) {
260 if (!dirstate_tuple_check(v)) {
260 if (!dirstate_tuple_check(v)) {
261 PyErr_SetString(PyExc_TypeError,
261 PyErr_SetString(PyExc_TypeError,
262 "expected a dirstate tuple");
262 "expected a dirstate tuple");
263 goto quit;
263 goto quit;
264 }
264 }
265
265
266 tuple = (dirstateItemObject *)v;
266 tuple = (dirstateItemObject *)v;
267 if (tuple->state != 'r') {
267 if (tuple->flags | dirstate_flag_wc_tracked) {
268 PyObject *normed;
268 PyObject *normed;
269 if (table != NULL) {
269 if (table != NULL) {
270 normed = _asciitransform(k, table,
270 normed = _asciitransform(k, table,
271 normcase_fallback);
271 normcase_fallback);
272 } else {
272 } else {
273 normed = PyObject_CallFunctionObjArgs(
273 normed = PyObject_CallFunctionObjArgs(
274 normcase_fallback, k, NULL);
274 normcase_fallback, k, NULL);
275 }
275 }
276
276
277 if (normed == NULL) {
277 if (normed == NULL) {
278 goto quit;
278 goto quit;
279 }
279 }
280 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
280 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
281 Py_DECREF(normed);
281 Py_DECREF(normed);
282 goto quit;
282 goto quit;
283 }
283 }
284 Py_DECREF(normed);
284 Py_DECREF(normed);
285 }
285 }
286 }
286 }
287 return file_foldmap;
287 return file_foldmap;
288 quit:
288 quit:
289 Py_XDECREF(file_foldmap);
289 Py_XDECREF(file_foldmap);
290 return NULL;
290 return NULL;
291 }
291 }
292
292
293 /* calculate length of JSON-escaped string; returns -1 if unsupported */
293 /* calculate length of JSON-escaped string; returns -1 if unsupported */
294 static Py_ssize_t jsonescapelen(const char *buf, Py_ssize_t len, bool paranoid)
294 static Py_ssize_t jsonescapelen(const char *buf, Py_ssize_t len, bool paranoid)
295 {
295 {
296 Py_ssize_t i, esclen = 0;
296 Py_ssize_t i, esclen = 0;
297
297
298 if (paranoid) {
298 if (paranoid) {
299 /* don't want to process multi-byte escapes in C */
299 /* don't want to process multi-byte escapes in C */
300 for (i = 0; i < len; i++) {
300 for (i = 0; i < len; i++) {
301 char c = buf[i];
301 char c = buf[i];
302 if (c & 0x80) {
302 if (c & 0x80) {
303 PyErr_SetString(PyExc_ValueError,
303 PyErr_SetString(PyExc_ValueError,
304 "cannot process non-ascii str");
304 "cannot process non-ascii str");
305 return -1;
305 return -1;
306 }
306 }
307 esclen += jsonparanoidlentable[(unsigned char)c];
307 esclen += jsonparanoidlentable[(unsigned char)c];
308 if (esclen < 0) {
308 if (esclen < 0) {
309 PyErr_SetString(PyExc_MemoryError,
309 PyErr_SetString(PyExc_MemoryError,
310 "overflow in jsonescapelen");
310 "overflow in jsonescapelen");
311 return -1;
311 return -1;
312 }
312 }
313 }
313 }
314 } else {
314 } else {
315 for (i = 0; i < len; i++) {
315 for (i = 0; i < len; i++) {
316 char c = buf[i];
316 char c = buf[i];
317 esclen += jsonlentable[(unsigned char)c];
317 esclen += jsonlentable[(unsigned char)c];
318 if (esclen < 0) {
318 if (esclen < 0) {
319 PyErr_SetString(PyExc_MemoryError,
319 PyErr_SetString(PyExc_MemoryError,
320 "overflow in jsonescapelen");
320 "overflow in jsonescapelen");
321 return -1;
321 return -1;
322 }
322 }
323 }
323 }
324 }
324 }
325
325
326 return esclen;
326 return esclen;
327 }
327 }
328
328
329 /* map '\<c>' escape character */
329 /* map '\<c>' escape character */
330 static char jsonescapechar2(char c)
330 static char jsonescapechar2(char c)
331 {
331 {
332 switch (c) {
332 switch (c) {
333 case '\b':
333 case '\b':
334 return 'b';
334 return 'b';
335 case '\t':
335 case '\t':
336 return 't';
336 return 't';
337 case '\n':
337 case '\n':
338 return 'n';
338 return 'n';
339 case '\f':
339 case '\f':
340 return 'f';
340 return 'f';
341 case '\r':
341 case '\r':
342 return 'r';
342 return 'r';
343 case '"':
343 case '"':
344 return '"';
344 return '"';
345 case '\\':
345 case '\\':
346 return '\\';
346 return '\\';
347 }
347 }
348 return '\0'; /* should not happen */
348 return '\0'; /* should not happen */
349 }
349 }
350
350
351 /* convert 'origbuf' to JSON-escaped form 'escbuf'; 'origbuf' should only
351 /* convert 'origbuf' to JSON-escaped form 'escbuf'; 'origbuf' should only
352 include characters mappable by json(paranoid)lentable */
352 include characters mappable by json(paranoid)lentable */
353 static void encodejsonescape(char *escbuf, Py_ssize_t esclen,
353 static void encodejsonescape(char *escbuf, Py_ssize_t esclen,
354 const char *origbuf, Py_ssize_t origlen,
354 const char *origbuf, Py_ssize_t origlen,
355 bool paranoid)
355 bool paranoid)
356 {
356 {
357 const uint8_t *lentable =
357 const uint8_t *lentable =
358 (paranoid) ? jsonparanoidlentable : jsonlentable;
358 (paranoid) ? jsonparanoidlentable : jsonlentable;
359 Py_ssize_t i, j;
359 Py_ssize_t i, j;
360
360
361 for (i = 0, j = 0; i < origlen; i++) {
361 for (i = 0, j = 0; i < origlen; i++) {
362 char c = origbuf[i];
362 char c = origbuf[i];
363 uint8_t l = lentable[(unsigned char)c];
363 uint8_t l = lentable[(unsigned char)c];
364 assert(j + l <= esclen);
364 assert(j + l <= esclen);
365 switch (l) {
365 switch (l) {
366 case 1:
366 case 1:
367 escbuf[j] = c;
367 escbuf[j] = c;
368 break;
368 break;
369 case 2:
369 case 2:
370 escbuf[j] = '\\';
370 escbuf[j] = '\\';
371 escbuf[j + 1] = jsonescapechar2(c);
371 escbuf[j + 1] = jsonescapechar2(c);
372 break;
372 break;
373 case 6:
373 case 6:
374 memcpy(escbuf + j, "\\u00", 4);
374 memcpy(escbuf + j, "\\u00", 4);
375 escbuf[j + 4] = hexchartable[(unsigned char)c >> 4];
375 escbuf[j + 4] = hexchartable[(unsigned char)c >> 4];
376 escbuf[j + 5] = hexchartable[(unsigned char)c & 0xf];
376 escbuf[j + 5] = hexchartable[(unsigned char)c & 0xf];
377 break;
377 break;
378 }
378 }
379 j += l;
379 j += l;
380 }
380 }
381 }
381 }
382
382
383 PyObject *jsonescapeu8fast(PyObject *self, PyObject *args)
383 PyObject *jsonescapeu8fast(PyObject *self, PyObject *args)
384 {
384 {
385 PyObject *origstr, *escstr;
385 PyObject *origstr, *escstr;
386 const char *origbuf;
386 const char *origbuf;
387 Py_ssize_t origlen, esclen;
387 Py_ssize_t origlen, esclen;
388 int paranoid;
388 int paranoid;
389 if (!PyArg_ParseTuple(args, "O!i:jsonescapeu8fast", &PyBytes_Type,
389 if (!PyArg_ParseTuple(args, "O!i:jsonescapeu8fast", &PyBytes_Type,
390 &origstr, &paranoid)) {
390 &origstr, &paranoid)) {
391 return NULL;
391 return NULL;
392 }
392 }
393
393
394 origbuf = PyBytes_AS_STRING(origstr);
394 origbuf = PyBytes_AS_STRING(origstr);
395 origlen = PyBytes_GET_SIZE(origstr);
395 origlen = PyBytes_GET_SIZE(origstr);
396 esclen = jsonescapelen(origbuf, origlen, paranoid);
396 esclen = jsonescapelen(origbuf, origlen, paranoid);
397 if (esclen < 0) {
397 if (esclen < 0) {
398 return NULL; /* unsupported char found or overflow */
398 return NULL; /* unsupported char found or overflow */
399 }
399 }
400 if (origlen == esclen) {
400 if (origlen == esclen) {
401 Py_INCREF(origstr);
401 Py_INCREF(origstr);
402 return origstr;
402 return origstr;
403 }
403 }
404
404
405 escstr = PyBytes_FromStringAndSize(NULL, esclen);
405 escstr = PyBytes_FromStringAndSize(NULL, esclen);
406 if (!escstr) {
406 if (!escstr) {
407 return NULL;
407 return NULL;
408 }
408 }
409 encodejsonescape(PyBytes_AS_STRING(escstr), esclen, origbuf, origlen,
409 encodejsonescape(PyBytes_AS_STRING(escstr), esclen, origbuf, origlen,
410 paranoid);
410 paranoid);
411
411
412 return escstr;
412 return escstr;
413 }
413 }
@@ -1,329 +1,330 b''
1 /*
1 /*
2 dirs.c - dynamic directory diddling for dirstates
2 dirs.c - dynamic directory diddling for dirstates
3
3
4 Copyright 2013 Facebook
4 Copyright 2013 Facebook
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
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 <string.h>
13
13
14 #include "util.h"
14 #include "util.h"
15
15
16 #ifdef IS_PY3K
16 #ifdef IS_PY3K
17 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[0]
17 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[0]
18 #else
18 #else
19 #define PYLONG_VALUE(o) PyInt_AS_LONG(o)
19 #define PYLONG_VALUE(o) PyInt_AS_LONG(o)
20 #endif
20 #endif
21
21
22 /*
22 /*
23 * This is a multiset of directory names, built from the files that
23 * This is a multiset of directory names, built from the files that
24 * appear in a dirstate or manifest.
24 * appear in a dirstate or manifest.
25 *
25 *
26 * A few implementation notes:
26 * A few implementation notes:
27 *
27 *
28 * We modify Python integers for refcounting, but those integers are
28 * We modify Python integers for refcounting, but those integers are
29 * never visible to Python code.
29 * never visible to Python code.
30 */
30 */
31 /* clang-format off */
31 /* clang-format off */
32 typedef struct {
32 typedef struct {
33 PyObject_HEAD
33 PyObject_HEAD
34 PyObject *dict;
34 PyObject *dict;
35 } dirsObject;
35 } dirsObject;
36 /* clang-format on */
36 /* clang-format on */
37
37
38 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
38 static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
39 {
39 {
40 while (pos != -1) {
40 while (pos != -1) {
41 if (path[pos] == '/')
41 if (path[pos] == '/')
42 break;
42 break;
43 pos -= 1;
43 pos -= 1;
44 }
44 }
45 if (pos == -1) {
45 if (pos == -1) {
46 return 0;
46 return 0;
47 }
47 }
48
48
49 return pos;
49 return pos;
50 }
50 }
51
51
52 /* Mercurial will fail to run on directory hierarchies deeper than
52 /* Mercurial will fail to run on directory hierarchies deeper than
53 * this constant, so we should try and keep this constant as big as
53 * this constant, so we should try and keep this constant as big as
54 * possible.
54 * possible.
55 */
55 */
56 #define MAX_DIRS_DEPTH 2048
56 #define MAX_DIRS_DEPTH 2048
57
57
58 static int _addpath(PyObject *dirs, PyObject *path)
58 static int _addpath(PyObject *dirs, PyObject *path)
59 {
59 {
60 const char *cpath = PyBytes_AS_STRING(path);
60 const char *cpath = PyBytes_AS_STRING(path);
61 Py_ssize_t pos = PyBytes_GET_SIZE(path);
61 Py_ssize_t pos = PyBytes_GET_SIZE(path);
62 PyObject *key = NULL;
62 PyObject *key = NULL;
63 int ret = -1;
63 int ret = -1;
64 size_t num_slashes = 0;
64 size_t num_slashes = 0;
65
65
66 /* This loop is super critical for performance. That's why we inline
66 /* This loop is super critical for performance. That's why we inline
67 * access to Python structs instead of going through a supported API.
67 * access to Python structs instead of going through a supported API.
68 * The implementation, therefore, is heavily dependent on CPython
68 * The implementation, therefore, is heavily dependent on CPython
69 * implementation details. We also commit violations of the Python
69 * implementation details. We also commit violations of the Python
70 * "protocol" such as mutating immutable objects. But since we only
70 * "protocol" such as mutating immutable objects. But since we only
71 * mutate objects created in this function or in other well-defined
71 * mutate objects created in this function or in other well-defined
72 * locations, the references are known so these violations should go
72 * locations, the references are known so these violations should go
73 * unnoticed. */
73 * unnoticed. */
74 while ((pos = _finddir(cpath, pos - 1)) != -1) {
74 while ((pos = _finddir(cpath, pos - 1)) != -1) {
75 PyObject *val;
75 PyObject *val;
76 ++num_slashes;
76 ++num_slashes;
77 if (num_slashes > MAX_DIRS_DEPTH) {
77 if (num_slashes > MAX_DIRS_DEPTH) {
78 PyErr_SetString(PyExc_ValueError,
78 PyErr_SetString(PyExc_ValueError,
79 "Directory hierarchy too deep.");
79 "Directory hierarchy too deep.");
80 goto bail;
80 goto bail;
81 }
81 }
82
82
83 /* Sniff for trailing slashes, a marker of an invalid input. */
83 /* Sniff for trailing slashes, a marker of an invalid input. */
84 if (pos > 0 && cpath[pos - 1] == '/') {
84 if (pos > 0 && cpath[pos - 1] == '/') {
85 PyErr_SetString(
85 PyErr_SetString(
86 PyExc_ValueError,
86 PyExc_ValueError,
87 "found invalid consecutive slashes in path");
87 "found invalid consecutive slashes in path");
88 goto bail;
88 goto bail;
89 }
89 }
90
90
91 key = PyBytes_FromStringAndSize(cpath, pos);
91 key = PyBytes_FromStringAndSize(cpath, pos);
92 if (key == NULL)
92 if (key == NULL)
93 goto bail;
93 goto bail;
94
94
95 val = PyDict_GetItem(dirs, key);
95 val = PyDict_GetItem(dirs, key);
96 if (val != NULL) {
96 if (val != NULL) {
97 PYLONG_VALUE(val) += 1;
97 PYLONG_VALUE(val) += 1;
98 Py_CLEAR(key);
98 Py_CLEAR(key);
99 break;
99 break;
100 }
100 }
101
101
102 /* Force Python to not reuse a small shared int. */
102 /* Force Python to not reuse a small shared int. */
103 #ifdef IS_PY3K
103 #ifdef IS_PY3K
104 val = PyLong_FromLong(0x1eadbeef);
104 val = PyLong_FromLong(0x1eadbeef);
105 #else
105 #else
106 val = PyInt_FromLong(0x1eadbeef);
106 val = PyInt_FromLong(0x1eadbeef);
107 #endif
107 #endif
108
108
109 if (val == NULL)
109 if (val == NULL)
110 goto bail;
110 goto bail;
111
111
112 PYLONG_VALUE(val) = 1;
112 PYLONG_VALUE(val) = 1;
113 ret = PyDict_SetItem(dirs, key, val);
113 ret = PyDict_SetItem(dirs, key, val);
114 Py_DECREF(val);
114 Py_DECREF(val);
115 if (ret == -1)
115 if (ret == -1)
116 goto bail;
116 goto bail;
117 Py_CLEAR(key);
117 Py_CLEAR(key);
118 }
118 }
119 ret = 0;
119 ret = 0;
120
120
121 bail:
121 bail:
122 Py_XDECREF(key);
122 Py_XDECREF(key);
123
123
124 return ret;
124 return ret;
125 }
125 }
126
126
127 static int _delpath(PyObject *dirs, PyObject *path)
127 static int _delpath(PyObject *dirs, PyObject *path)
128 {
128 {
129 char *cpath = PyBytes_AS_STRING(path);
129 char *cpath = PyBytes_AS_STRING(path);
130 Py_ssize_t pos = PyBytes_GET_SIZE(path);
130 Py_ssize_t pos = PyBytes_GET_SIZE(path);
131 PyObject *key = NULL;
131 PyObject *key = NULL;
132 int ret = -1;
132 int ret = -1;
133
133
134 while ((pos = _finddir(cpath, pos - 1)) != -1) {
134 while ((pos = _finddir(cpath, pos - 1)) != -1) {
135 PyObject *val;
135 PyObject *val;
136
136
137 key = PyBytes_FromStringAndSize(cpath, pos);
137 key = PyBytes_FromStringAndSize(cpath, pos);
138
138
139 if (key == NULL)
139 if (key == NULL)
140 goto bail;
140 goto bail;
141
141
142 val = PyDict_GetItem(dirs, key);
142 val = PyDict_GetItem(dirs, key);
143 if (val == NULL) {
143 if (val == NULL) {
144 PyErr_SetString(PyExc_ValueError,
144 PyErr_SetString(PyExc_ValueError,
145 "expected a value, found none");
145 "expected a value, found none");
146 goto bail;
146 goto bail;
147 }
147 }
148
148
149 if (--PYLONG_VALUE(val) <= 0) {
149 if (--PYLONG_VALUE(val) <= 0) {
150 if (PyDict_DelItem(dirs, key) == -1)
150 if (PyDict_DelItem(dirs, key) == -1)
151 goto bail;
151 goto bail;
152 } else
152 } else
153 break;
153 break;
154 Py_CLEAR(key);
154 Py_CLEAR(key);
155 }
155 }
156 ret = 0;
156 ret = 0;
157
157
158 bail:
158 bail:
159 Py_XDECREF(key);
159 Py_XDECREF(key);
160
160
161 return ret;
161 return ret;
162 }
162 }
163
163
164 static int dirs_fromdict(PyObject *dirs, PyObject *source, bool only_tracked)
164 static int dirs_fromdict(PyObject *dirs, PyObject *source, bool only_tracked)
165 {
165 {
166 PyObject *key, *value;
166 PyObject *key, *value;
167 Py_ssize_t pos = 0;
167 Py_ssize_t pos = 0;
168
168
169 while (PyDict_Next(source, &pos, &key, &value)) {
169 while (PyDict_Next(source, &pos, &key, &value)) {
170 if (!PyBytes_Check(key)) {
170 if (!PyBytes_Check(key)) {
171 PyErr_SetString(PyExc_TypeError, "expected string key");
171 PyErr_SetString(PyExc_TypeError, "expected string key");
172 return -1;
172 return -1;
173 }
173 }
174 if (only_tracked) {
174 if (only_tracked) {
175 if (!dirstate_tuple_check(value)) {
175 if (!dirstate_tuple_check(value)) {
176 PyErr_SetString(PyExc_TypeError,
176 PyErr_SetString(PyExc_TypeError,
177 "expected a dirstate tuple");
177 "expected a dirstate tuple");
178 return -1;
178 return -1;
179 }
179 }
180 if (((dirstateItemObject *)value)->state == 'r')
180 if (!(((dirstateItemObject *)value)->flags &
181 dirstate_flag_wc_tracked))
181 continue;
182 continue;
182 }
183 }
183
184
184 if (_addpath(dirs, key) == -1)
185 if (_addpath(dirs, key) == -1)
185 return -1;
186 return -1;
186 }
187 }
187
188
188 return 0;
189 return 0;
189 }
190 }
190
191
191 static int dirs_fromiter(PyObject *dirs, PyObject *source)
192 static int dirs_fromiter(PyObject *dirs, PyObject *source)
192 {
193 {
193 PyObject *iter, *item = NULL;
194 PyObject *iter, *item = NULL;
194 int ret;
195 int ret;
195
196
196 iter = PyObject_GetIter(source);
197 iter = PyObject_GetIter(source);
197 if (iter == NULL)
198 if (iter == NULL)
198 return -1;
199 return -1;
199
200
200 while ((item = PyIter_Next(iter)) != NULL) {
201 while ((item = PyIter_Next(iter)) != NULL) {
201 if (!PyBytes_Check(item)) {
202 if (!PyBytes_Check(item)) {
202 PyErr_SetString(PyExc_TypeError, "expected string");
203 PyErr_SetString(PyExc_TypeError, "expected string");
203 break;
204 break;
204 }
205 }
205
206
206 if (_addpath(dirs, item) == -1)
207 if (_addpath(dirs, item) == -1)
207 break;
208 break;
208 Py_CLEAR(item);
209 Py_CLEAR(item);
209 }
210 }
210
211
211 ret = PyErr_Occurred() ? -1 : 0;
212 ret = PyErr_Occurred() ? -1 : 0;
212 Py_DECREF(iter);
213 Py_DECREF(iter);
213 Py_XDECREF(item);
214 Py_XDECREF(item);
214 return ret;
215 return ret;
215 }
216 }
216
217
217 /*
218 /*
218 * Calculate a refcounted set of directory names for the files in a
219 * Calculate a refcounted set of directory names for the files in a
219 * dirstate.
220 * dirstate.
220 */
221 */
221 static int dirs_init(dirsObject *self, PyObject *args, PyObject *kwargs)
222 static int dirs_init(dirsObject *self, PyObject *args, PyObject *kwargs)
222 {
223 {
223 PyObject *dirs = NULL, *source = NULL;
224 PyObject *dirs = NULL, *source = NULL;
224 int only_tracked = 0;
225 int only_tracked = 0;
225 int ret = -1;
226 int ret = -1;
226 static char *keywords_name[] = {"map", "only_tracked", NULL};
227 static char *keywords_name[] = {"map", "only_tracked", NULL};
227
228
228 self->dict = NULL;
229 self->dict = NULL;
229
230
230 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:__init__",
231 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:__init__",
231 keywords_name, &source, &only_tracked))
232 keywords_name, &source, &only_tracked))
232 return -1;
233 return -1;
233
234
234 dirs = PyDict_New();
235 dirs = PyDict_New();
235
236
236 if (dirs == NULL)
237 if (dirs == NULL)
237 return -1;
238 return -1;
238
239
239 if (source == NULL)
240 if (source == NULL)
240 ret = 0;
241 ret = 0;
241 else if (PyDict_Check(source))
242 else if (PyDict_Check(source))
242 ret = dirs_fromdict(dirs, source, (bool)only_tracked);
243 ret = dirs_fromdict(dirs, source, (bool)only_tracked);
243 else if (only_tracked)
244 else if (only_tracked)
244 PyErr_SetString(PyExc_ValueError,
245 PyErr_SetString(PyExc_ValueError,
245 "`only_tracked` is only supported "
246 "`only_tracked` is only supported "
246 "with a dict source");
247 "with a dict source");
247 else
248 else
248 ret = dirs_fromiter(dirs, source);
249 ret = dirs_fromiter(dirs, source);
249
250
250 if (ret == -1)
251 if (ret == -1)
251 Py_XDECREF(dirs);
252 Py_XDECREF(dirs);
252 else
253 else
253 self->dict = dirs;
254 self->dict = dirs;
254
255
255 return ret;
256 return ret;
256 }
257 }
257
258
258 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
259 PyObject *dirs_addpath(dirsObject *self, PyObject *args)
259 {
260 {
260 PyObject *path;
261 PyObject *path;
261
262
262 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
263 if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
263 return NULL;
264 return NULL;
264
265
265 if (_addpath(self->dict, path) == -1)
266 if (_addpath(self->dict, path) == -1)
266 return NULL;
267 return NULL;
267
268
268 Py_RETURN_NONE;
269 Py_RETURN_NONE;
269 }
270 }
270
271
271 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
272 static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
272 {
273 {
273 PyObject *path;
274 PyObject *path;
274
275
275 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
276 if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
276 return NULL;
277 return NULL;
277
278
278 if (_delpath(self->dict, path) == -1)
279 if (_delpath(self->dict, path) == -1)
279 return NULL;
280 return NULL;
280
281
281 Py_RETURN_NONE;
282 Py_RETURN_NONE;
282 }
283 }
283
284
284 static int dirs_contains(dirsObject *self, PyObject *value)
285 static int dirs_contains(dirsObject *self, PyObject *value)
285 {
286 {
286 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
287 return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
287 }
288 }
288
289
289 static void dirs_dealloc(dirsObject *self)
290 static void dirs_dealloc(dirsObject *self)
290 {
291 {
291 Py_XDECREF(self->dict);
292 Py_XDECREF(self->dict);
292 PyObject_Del(self);
293 PyObject_Del(self);
293 }
294 }
294
295
295 static PyObject *dirs_iter(dirsObject *self)
296 static PyObject *dirs_iter(dirsObject *self)
296 {
297 {
297 return PyObject_GetIter(self->dict);
298 return PyObject_GetIter(self->dict);
298 }
299 }
299
300
300 static PySequenceMethods dirs_sequence_methods;
301 static PySequenceMethods dirs_sequence_methods;
301
302
302 static PyMethodDef dirs_methods[] = {
303 static PyMethodDef dirs_methods[] = {
303 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
304 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
304 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
305 {"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
305 {NULL} /* Sentinel */
306 {NULL} /* Sentinel */
306 };
307 };
307
308
308 static PyTypeObject dirsType = {PyVarObject_HEAD_INIT(NULL, 0)};
309 static PyTypeObject dirsType = {PyVarObject_HEAD_INIT(NULL, 0)};
309
310
310 void dirs_module_init(PyObject *mod)
311 void dirs_module_init(PyObject *mod)
311 {
312 {
312 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
313 dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
313 dirsType.tp_name = "parsers.dirs";
314 dirsType.tp_name = "parsers.dirs";
314 dirsType.tp_new = PyType_GenericNew;
315 dirsType.tp_new = PyType_GenericNew;
315 dirsType.tp_basicsize = sizeof(dirsObject);
316 dirsType.tp_basicsize = sizeof(dirsObject);
316 dirsType.tp_dealloc = (destructor)dirs_dealloc;
317 dirsType.tp_dealloc = (destructor)dirs_dealloc;
317 dirsType.tp_as_sequence = &dirs_sequence_methods;
318 dirsType.tp_as_sequence = &dirs_sequence_methods;
318 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
319 dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
319 dirsType.tp_doc = "dirs";
320 dirsType.tp_doc = "dirs";
320 dirsType.tp_iter = (getiterfunc)dirs_iter;
321 dirsType.tp_iter = (getiterfunc)dirs_iter;
321 dirsType.tp_methods = dirs_methods;
322 dirsType.tp_methods = dirs_methods;
322 dirsType.tp_init = (initproc)dirs_init;
323 dirsType.tp_init = (initproc)dirs_init;
323
324
324 if (PyType_Ready(&dirsType) < 0)
325 if (PyType_Ready(&dirsType) < 0)
325 return;
326 return;
326 Py_INCREF(&dirsType);
327 Py_INCREF(&dirsType);
327
328
328 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
329 PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
329 }
330 }
@@ -1,1193 +1,1303 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
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 <ctype.h>
12 #include <ctype.h>
13 #include <stddef.h>
13 #include <stddef.h>
14 #include <string.h>
14 #include <string.h>
15
15
16 #include "bitmanipulation.h"
16 #include "bitmanipulation.h"
17 #include "charencode.h"
17 #include "charencode.h"
18 #include "util.h"
18 #include "util.h"
19
19
20 #ifdef IS_PY3K
20 #ifdef IS_PY3K
21 /* The mapping of Python types is meant to be temporary to get Python
21 /* The mapping of Python types is meant to be temporary to get Python
22 * 3 to compile. We should remove this once Python 3 support is fully
22 * 3 to compile. We should remove this once Python 3 support is fully
23 * supported and proper types are used in the extensions themselves. */
23 * supported and proper types are used in the extensions themselves. */
24 #define PyInt_Check PyLong_Check
24 #define PyInt_Check PyLong_Check
25 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromLong PyLong_FromLong
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
27 #define PyInt_AsLong PyLong_AsLong
27 #define PyInt_AsLong PyLong_AsLong
28 #endif
28 #endif
29
29
30 static const char *const versionerrortext = "Python minor version mismatch";
30 static const char *const versionerrortext = "Python minor version mismatch";
31
31
32 static const int dirstate_v1_from_p2 = -2;
32 static const int dirstate_v1_from_p2 = -2;
33 static const int dirstate_v1_nonnormal = -1;
33 static const int dirstate_v1_nonnormal = -1;
34 static const int ambiguous_time = -1;
34 static const int ambiguous_time = -1;
35
35
36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
37 {
37 {
38 Py_ssize_t expected_size;
38 Py_ssize_t expected_size;
39
39
40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
41 return NULL;
41 return NULL;
42 }
42 }
43
43
44 return _dict_new_presized(expected_size);
44 return _dict_new_presized(expected_size);
45 }
45 }
46
46
47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
48 PyObject *kwds)
48 PyObject *kwds)
49 {
49 {
50 /* We do all the initialization here and not a tp_init function because
50 /* We do all the initialization here and not a tp_init function because
51 * dirstate_item is immutable. */
51 * dirstate_item is immutable. */
52 dirstateItemObject *t;
52 dirstateItemObject *t;
53 int wc_tracked;
53 int wc_tracked;
54 int p1_tracked;
54 int p1_tracked;
55 int p2_tracked;
55 int p2_tracked;
56 int merged;
56 int merged;
57 int clean_p1;
57 int clean_p1;
58 int clean_p2;
58 int clean_p2;
59 int possibly_dirty;
59 int possibly_dirty;
60 PyObject *parentfiledata;
60 PyObject *parentfiledata;
61 static char *keywords_name[] = {
61 static char *keywords_name[] = {
62 "wc_tracked", "p1_tracked", "p2_tracked",
62 "wc_tracked", "p1_tracked", "p2_tracked",
63 "merged", "clean_p1", "clean_p2",
63 "merged", "clean_p1", "clean_p2",
64 "possibly_dirty", "parentfiledata", NULL,
64 "possibly_dirty", "parentfiledata", NULL,
65 };
65 };
66 wc_tracked = 0;
66 wc_tracked = 0;
67 p1_tracked = 0;
67 p1_tracked = 0;
68 p2_tracked = 0;
68 p2_tracked = 0;
69 merged = 0;
69 merged = 0;
70 clean_p1 = 0;
70 clean_p1 = 0;
71 clean_p2 = 0;
71 clean_p2 = 0;
72 possibly_dirty = 0;
72 possibly_dirty = 0;
73 parentfiledata = Py_None;
73 parentfiledata = Py_None;
74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name,
74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name,
75 &wc_tracked, &p1_tracked, &p2_tracked,
75 &wc_tracked, &p1_tracked, &p2_tracked,
76 &merged, &clean_p1, &clean_p2,
76 &merged, &clean_p1, &clean_p2,
77 &possibly_dirty, &parentfiledata
77 &possibly_dirty, &parentfiledata
78
78
79 )) {
79 )) {
80 return NULL;
80 return NULL;
81 }
81 }
82 if (merged && (clean_p1 || clean_p2)) {
82 if (merged && (clean_p1 || clean_p2)) {
83 PyErr_SetString(PyExc_RuntimeError,
83 PyErr_SetString(PyExc_RuntimeError,
84 "`merged` argument incompatible with "
84 "`merged` argument incompatible with "
85 "`clean_p1`/`clean_p2`");
85 "`clean_p1`/`clean_p2`");
86 return NULL;
86 return NULL;
87 }
87 }
88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
89 if (!t) {
89 if (!t) {
90 return NULL;
90 return NULL;
91 }
91 }
92 t->state = 'r';
92
93 t->flags = 0;
94 if (wc_tracked) {
95 t->flags |= dirstate_flag_wc_tracked;
96 }
97 if (p1_tracked) {
98 t->flags |= dirstate_flag_p1_tracked;
99 }
100 if (p2_tracked) {
101 t->flags |= dirstate_flag_p2_tracked;
102 }
103 if (possibly_dirty) {
104 t->flags |= dirstate_flag_possibly_dirty;
105 }
106 if (merged) {
107 t->flags |= dirstate_flag_merged;
108 }
109 if (clean_p1) {
110 t->flags |= dirstate_flag_clean_p1;
111 }
112 if (clean_p2) {
113 t->flags |= dirstate_flag_clean_p2;
114 }
93 t->mode = 0;
115 t->mode = 0;
94 t->size = dirstate_v1_nonnormal;
116 t->size = dirstate_v1_nonnormal;
95 t->mtime = ambiguous_time;
117 t->mtime = ambiguous_time;
96 if (!(p1_tracked || p2_tracked || wc_tracked)) {
118 if (parentfiledata != Py_None) {
97 /* Nothing special to do, file is untracked */
98 } else if (merged) {
99 t->state = 'm';
100 t->size = dirstate_v1_from_p2;
101 t->mtime = ambiguous_time;
102 } else if (!(p1_tracked || p2_tracked) && wc_tracked) {
103 t->state = 'a';
104 t->size = dirstate_v1_nonnormal;
105 t->mtime = ambiguous_time;
106 } else if ((p1_tracked || p2_tracked) && !wc_tracked) {
107 t->state = 'r';
108 t->size = 0;
109 t->mtime = 0;
110 } else if (clean_p2 && wc_tracked) {
111 t->state = 'n';
112 t->size = dirstate_v1_from_p2;
113 t->mtime = ambiguous_time;
114 } else if (!p1_tracked && p2_tracked && wc_tracked) {
115 t->state = 'n';
116 t->size = dirstate_v1_from_p2;
117 t->mtime = ambiguous_time;
118 } else if (possibly_dirty) {
119 t->state = 'n';
120 t->size = dirstate_v1_nonnormal;
121 t->mtime = ambiguous_time;
122 } else if (wc_tracked) {
123 /* this is a "normal" file */
124 if (parentfiledata == Py_None) {
125 PyErr_SetString(
126 PyExc_RuntimeError,
127 "failed to pass parentfiledata for a normal file");
128 return NULL;
129 }
130 if (!PyTuple_CheckExact(parentfiledata)) {
119 if (!PyTuple_CheckExact(parentfiledata)) {
131 PyErr_SetString(
120 PyErr_SetString(
132 PyExc_TypeError,
121 PyExc_TypeError,
133 "parentfiledata should be a Tuple or None");
122 "parentfiledata should be a Tuple or None");
134 return NULL;
123 return NULL;
135 }
124 }
136 t->state = 'n';
137 t->mode =
125 t->mode =
138 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
126 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
139 t->size =
127 t->size =
140 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
128 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
141 t->mtime =
129 t->mtime =
142 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
130 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
143 } else {
144 PyErr_SetString(PyExc_RuntimeError, "unreachable");
145 return NULL;
146 }
131 }
147 return (PyObject *)t;
132 return (PyObject *)t;
148 }
133 }
149
134
150 static void dirstate_item_dealloc(PyObject *o)
135 static void dirstate_item_dealloc(PyObject *o)
151 {
136 {
152 PyObject_Del(o);
137 PyObject_Del(o);
153 }
138 }
154
139
155 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
140 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
156 {
141 {
157 return (self->state == 'a' || self->state == 'm' || self->state == 'n');
142 return (self->flags & dirstate_flag_wc_tracked);
158 }
143 }
159
144
160 static inline bool dirstate_item_c_added(dirstateItemObject *self)
145 static inline bool dirstate_item_c_added(dirstateItemObject *self)
161 {
146 {
162 return (self->state == 'a');
147 char mask = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
148 dirstate_flag_p2_tracked);
149 char target = dirstate_flag_wc_tracked;
150 return (self->flags & mask) == target;
163 }
151 }
164
152
165 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
153 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
166 {
154 {
167 return (self->state == 'r');
155 if (self->flags & dirstate_flag_wc_tracked) {
156 return false;
157 }
158 return (self->flags &
159 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked));
168 }
160 }
169
161
170 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
162 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
171 {
163 {
172 return (self->state == 'm');
164 return ((self->flags & dirstate_flag_wc_tracked) &&
165 (self->flags & dirstate_flag_merged));
173 }
166 }
174
167
175 static inline bool dirstate_item_c_merged_removed(dirstateItemObject *self)
168 static inline bool dirstate_item_c_merged_removed(dirstateItemObject *self)
176 {
169 {
177 return (self->state == 'r' && self->size == dirstate_v1_nonnormal);
170 if (!dirstate_item_c_removed(self)) {
171 return false;
172 }
173 return (self->flags & dirstate_flag_merged);
178 }
174 }
179
175
180 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
176 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
181 {
177 {
182 return (self->state == 'n' && self->size == dirstate_v1_from_p2);
178 if (!dirstate_item_c_tracked(self)) {
179 return false;
180 }
181 return (self->flags & dirstate_flag_clean_p2);
183 }
182 }
184
183
185 static inline bool dirstate_item_c_from_p2_removed(dirstateItemObject *self)
184 static inline bool dirstate_item_c_from_p2_removed(dirstateItemObject *self)
186 {
185 {
187 return (self->state == 'r' && self->size == dirstate_v1_from_p2);
186 if (!dirstate_item_c_removed(self)) {
187 return false;
188 }
189 return (self->flags & dirstate_flag_clean_p2);
188 }
190 }
189
191
190 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
192 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
191 {
193 {
192 return self->state;
194 if (self->flags & dirstate_flag_rust_special) {
195 return ' ';
196 } else if (dirstate_item_c_removed(self)) {
197 return 'r';
198 } else if (dirstate_item_c_merged(self)) {
199 return 'm';
200 } else if (dirstate_item_c_added(self)) {
201 return 'a';
202 } else {
203 return 'n';
204 }
193 }
205 }
194
206
195 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
207 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
196 {
208 {
197 return self->mode;
209 return self->mode;
198 }
210 }
199
211
200 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
212 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
201 {
213 {
202 return self->size;
214 if (self->flags & dirstate_flag_rust_special) {
215 return self->size;
216 } else if (dirstate_item_c_merged_removed(self)) {
217 return dirstate_v1_nonnormal;
218 } else if (dirstate_item_c_from_p2_removed(self)) {
219 return dirstate_v1_from_p2;
220 } else if (dirstate_item_c_removed(self)) {
221 return 0;
222 } else if (dirstate_item_c_merged(self)) {
223 return dirstate_v1_from_p2;
224 } else if (dirstate_item_c_added(self)) {
225 return dirstate_v1_nonnormal;
226 } else if (dirstate_item_c_from_p2(self)) {
227 return dirstate_v1_from_p2;
228 } else if (self->flags & dirstate_flag_possibly_dirty) {
229 return self->size; /* NON NORMAL ? */
230 } else {
231 return self->size;
232 }
203 }
233 }
204
234
205 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
235 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
206 {
236 {
207 return self->mtime;
237 if (self->flags & dirstate_flag_rust_special) {
238 return self->mtime;
239 } else if (dirstate_item_c_removed(self)) {
240 return 0;
241 } else if (self->flags & dirstate_flag_possibly_dirty) {
242 return ambiguous_time;
243 } else if (dirstate_item_c_merged(self)) {
244 return ambiguous_time;
245 } else if (dirstate_item_c_added(self)) {
246 return ambiguous_time;
247 } else if (dirstate_item_c_from_p2(self)) {
248 return ambiguous_time;
249 } else {
250 return self->mtime;
251 }
208 }
252 }
209
253
210 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
254 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
211 {
255 {
212 char state = dirstate_item_c_v1_state(self);
256 char state = dirstate_item_c_v1_state(self);
213 return PyBytes_FromStringAndSize(&state, 1);
257 return PyBytes_FromStringAndSize(&state, 1);
214 };
258 };
215
259
216 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
260 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
217 {
261 {
218 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
262 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
219 };
263 };
220
264
221 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
265 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
222 {
266 {
223 return PyInt_FromLong(dirstate_item_c_v1_size(self));
267 return PyInt_FromLong(dirstate_item_c_v1_size(self));
224 };
268 };
225
269
226 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
270 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
227 {
271 {
228 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
272 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
229 };
273 };
230
274
231 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
275 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
232 PyObject *value)
276 PyObject *value)
233 {
277 {
234 long now;
278 long now;
235 if (!pylong_to_long(value, &now)) {
279 if (!pylong_to_long(value, &now)) {
236 return NULL;
280 return NULL;
237 }
281 }
238 if (dirstate_item_c_v1_state(self) == 'n' &&
282 if (dirstate_item_c_v1_state(self) == 'n' &&
239 dirstate_item_c_v1_mtime(self) == now) {
283 dirstate_item_c_v1_mtime(self) == now) {
240 Py_RETURN_TRUE;
284 Py_RETURN_TRUE;
241 } else {
285 } else {
242 Py_RETURN_FALSE;
286 Py_RETURN_FALSE;
243 }
287 }
244 };
288 };
245
289
246 /* This will never change since it's bound to V1
290 /* This will never change since it's bound to V1
247 */
291 */
248 static inline dirstateItemObject *
292 static inline dirstateItemObject *
249 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
293 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
250 {
294 {
251 dirstateItemObject *t =
295 dirstateItemObject *t =
252 PyObject_New(dirstateItemObject, &dirstateItemType);
296 PyObject_New(dirstateItemObject, &dirstateItemType);
253 if (!t) {
297 if (!t) {
254 return NULL;
298 return NULL;
255 }
299 }
256 t->state = state;
300
257 t->mode = mode;
301 if (state == 'm') {
258 t->size = size;
302 t->flags =
259 t->mtime = mtime;
303 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
304 dirstate_flag_p2_tracked | dirstate_flag_merged);
305 t->mode = 0;
306 t->size = dirstate_v1_from_p2;
307 t->mtime = ambiguous_time;
308 } else if (state == 'a') {
309 t->flags = dirstate_flag_wc_tracked;
310 t->mode = 0;
311 t->size = dirstate_v1_nonnormal;
312 t->mtime = ambiguous_time;
313 } else if (state == 'r') {
314 t->mode = 0;
315 t->size = 0;
316 t->mtime = 0;
317 if (size == dirstate_v1_nonnormal) {
318 t->flags =
319 (dirstate_flag_p1_tracked |
320 dirstate_flag_p2_tracked | dirstate_flag_merged);
321 } else if (size == dirstate_v1_from_p2) {
322 t->flags =
323 (dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
324 } else {
325 t->flags = dirstate_flag_p1_tracked;
326 }
327 } else if (state == 'n') {
328 if (size == dirstate_v1_from_p2) {
329 t->flags =
330 (dirstate_flag_wc_tracked |
331 dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
332 t->mode = 0;
333 t->size = dirstate_v1_from_p2;
334 t->mtime = ambiguous_time;
335 } else if (size == dirstate_v1_nonnormal) {
336 t->flags = (dirstate_flag_wc_tracked |
337 dirstate_flag_p1_tracked |
338 dirstate_flag_possibly_dirty);
339 t->mode = 0;
340 t->size = dirstate_v1_nonnormal;
341 t->mtime = ambiguous_time;
342 } else if (mtime == ambiguous_time) {
343 t->flags = (dirstate_flag_wc_tracked |
344 dirstate_flag_p1_tracked |
345 dirstate_flag_possibly_dirty);
346 t->mode = mode;
347 t->size = size;
348 t->mtime = 0;
349 } else {
350 t->flags = (dirstate_flag_wc_tracked |
351 dirstate_flag_p1_tracked);
352 t->mode = mode;
353 t->size = size;
354 t->mtime = mtime;
355 }
356 } else if (state == ' ') {
357 /* XXX Rust is using this special case, it should be clean up
358 * later. */
359 t->flags = dirstate_flag_rust_special;
360 t->mode = mode;
361 t->size = size;
362 t->mtime = mtime;
363 } else {
364 PyErr_Format(PyExc_RuntimeError,
365 "unknown state: `%c` (%d, %d, %d)", state, mode,
366 size, mtime, NULL);
367 return NULL;
368 }
369
260 return t;
370 return t;
261 }
371 }
262
372
263 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
373 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
264 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
374 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
265 PyObject *args)
375 PyObject *args)
266 {
376 {
267 /* We do all the initialization here and not a tp_init function because
377 /* We do all the initialization here and not a tp_init function because
268 * dirstate_item is immutable. */
378 * dirstate_item is immutable. */
269 char state;
379 char state;
270 int size, mode, mtime;
380 int size, mode, mtime;
271 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
381 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
272 return NULL;
382 return NULL;
273 }
383 }
274 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
384 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
275 };
385 };
276
386
277 /* constructor to help legacy API to build a new "added" item
387 /* constructor to help legacy API to build a new "added" item
278
388
279 Should eventually be removed */
389 Should eventually be removed */
280 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
390 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
281 {
391 {
282 dirstateItemObject *t;
392 dirstateItemObject *t;
283 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
393 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
284 if (!t) {
394 if (!t) {
285 return NULL;
395 return NULL;
286 }
396 }
287 t->state = 'a';
397 t->flags = dirstate_flag_wc_tracked;
288 t->mode = 0;
398 t->mode = 0;
289 t->size = dirstate_v1_nonnormal;
399 t->size = dirstate_v1_nonnormal;
290 t->mtime = ambiguous_time;
400 t->mtime = ambiguous_time;
291 return (PyObject *)t;
401 return (PyObject *)t;
292 };
402 };
293
403
294 /* constructor to help legacy API to build a new "merged" item
404 /* constructor to help legacy API to build a new "merged" item
295
405
296 Should eventually be removed */
406 Should eventually be removed */
297 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
407 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
298 {
408 {
299 dirstateItemObject *t;
409 dirstateItemObject *t;
300 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
410 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
301 if (!t) {
411 if (!t) {
302 return NULL;
412 return NULL;
303 }
413 }
304 t->state = 'm';
414 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
415 dirstate_flag_p2_tracked | dirstate_flag_merged);
305 t->mode = 0;
416 t->mode = 0;
306 t->size = dirstate_v1_from_p2;
417 t->size = dirstate_v1_from_p2;
307 t->mtime = ambiguous_time;
418 t->mtime = ambiguous_time;
308 return (PyObject *)t;
419 return (PyObject *)t;
309 };
420 };
310
421
311 /* constructor to help legacy API to build a new "from_p2" item
422 /* constructor to help legacy API to build a new "from_p2" item
312
423
313 Should eventually be removed */
424 Should eventually be removed */
314 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
425 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
315 {
426 {
316 /* We do all the initialization here and not a tp_init function because
427 /* We do all the initialization here and not a tp_init function because
317 * dirstate_item is immutable. */
428 * dirstate_item is immutable. */
318 dirstateItemObject *t;
429 dirstateItemObject *t;
319 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
430 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
320 if (!t) {
431 if (!t) {
321 return NULL;
432 return NULL;
322 }
433 }
323 t->state = 'n';
434 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked |
435 dirstate_flag_clean_p2);
324 t->mode = 0;
436 t->mode = 0;
325 t->size = dirstate_v1_from_p2;
437 t->size = dirstate_v1_from_p2;
326 t->mtime = ambiguous_time;
438 t->mtime = ambiguous_time;
327 return (PyObject *)t;
439 return (PyObject *)t;
328 };
440 };
329
441
330 /* constructor to help legacy API to build a new "possibly" item
442 /* constructor to help legacy API to build a new "possibly" item
331
443
332 Should eventually be removed */
444 Should eventually be removed */
333 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
445 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
334 {
446 {
335 /* We do all the initialization here and not a tp_init function because
447 /* We do all the initialization here and not a tp_init function because
336 * dirstate_item is immutable. */
448 * dirstate_item is immutable. */
337 dirstateItemObject *t;
449 dirstateItemObject *t;
338 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
450 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
339 if (!t) {
451 if (!t) {
340 return NULL;
452 return NULL;
341 }
453 }
342 t->state = 'n';
454 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
455 dirstate_flag_possibly_dirty);
343 t->mode = 0;
456 t->mode = 0;
344 t->size = dirstate_v1_nonnormal;
457 t->size = dirstate_v1_nonnormal;
345 t->mtime = ambiguous_time;
458 t->mtime = ambiguous_time;
346 return (PyObject *)t;
459 return (PyObject *)t;
347 };
460 };
348
461
349 /* constructor to help legacy API to build a new "normal" item
462 /* constructor to help legacy API to build a new "normal" item
350
463
351 Should eventually be removed */
464 Should eventually be removed */
352 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
465 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
353 {
466 {
354 /* We do all the initialization here and not a tp_init function because
467 /* We do all the initialization here and not a tp_init function because
355 * dirstate_item is immutable. */
468 * dirstate_item is immutable. */
356 dirstateItemObject *t;
469 dirstateItemObject *t;
357 int size, mode, mtime;
470 int size, mode, mtime;
358 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
471 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
359 return NULL;
472 return NULL;
360 }
473 }
361
474
362 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
475 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
363 if (!t) {
476 if (!t) {
364 return NULL;
477 return NULL;
365 }
478 }
366 t->state = 'n';
479 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked);
367 t->mode = mode;
480 t->mode = mode;
368 t->size = size;
481 t->size = size;
369 t->mtime = mtime;
482 t->mtime = mtime;
370 return (PyObject *)t;
483 return (PyObject *)t;
371 };
484 };
372
485
373 /* This means the next status call will have to actually check its content
486 /* This means the next status call will have to actually check its content
374 to make sure it is correct. */
487 to make sure it is correct. */
375 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
488 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
376 {
489 {
377 self->mtime = ambiguous_time;
490 if (self->flags |= dirstate_flag_possibly_dirty) {
378 Py_RETURN_NONE;
491 Py_RETURN_NONE;
492 }
379 }
493 }
380
494
381 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
495 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
382 {
496 {
383 if (self->state == 'm') {
497 self->flags &= ~dirstate_flag_wc_tracked;
384 self->size = dirstate_v1_nonnormal;
385 } else if (self->state == 'n' && self->size == dirstate_v1_from_p2) {
386 self->size = dirstate_v1_from_p2;
387 } else {
388 self->size = 0;
389 }
390 self->state = 'r';
391 self->mode = 0;
498 self->mode = 0;
392 self->mtime = 0;
499 self->mtime = 0;
500 self->size = 0;
393 Py_RETURN_NONE;
501 Py_RETURN_NONE;
394 }
502 }
395
503
396 static PyMethodDef dirstate_item_methods[] = {
504 static PyMethodDef dirstate_item_methods[] = {
397 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
505 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
398 "return a \"state\" suitable for v1 serialization"},
506 "return a \"state\" suitable for v1 serialization"},
399 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
507 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
400 "return a \"mode\" suitable for v1 serialization"},
508 "return a \"mode\" suitable for v1 serialization"},
401 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
509 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
402 "return a \"size\" suitable for v1 serialization"},
510 "return a \"size\" suitable for v1 serialization"},
403 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
511 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
404 "return a \"mtime\" suitable for v1 serialization"},
512 "return a \"mtime\" suitable for v1 serialization"},
405 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
513 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
406 "True if the stored mtime would be ambiguous with the current time"},
514 "True if the stored mtime would be ambiguous with the current time"},
407 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
515 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
408 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
516 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
409 {"new_added", (PyCFunction)dirstate_item_new_added,
517 {"new_added", (PyCFunction)dirstate_item_new_added,
410 METH_NOARGS | METH_CLASS,
518 METH_NOARGS | METH_CLASS,
411 "constructor to help legacy API to build a new \"added\" item"},
519 "constructor to help legacy API to build a new \"added\" item"},
412 {"new_merged", (PyCFunction)dirstate_item_new_merged,
520 {"new_merged", (PyCFunction)dirstate_item_new_merged,
413 METH_NOARGS | METH_CLASS,
521 METH_NOARGS | METH_CLASS,
414 "constructor to help legacy API to build a new \"merged\" item"},
522 "constructor to help legacy API to build a new \"merged\" item"},
415 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
523 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
416 METH_NOARGS | METH_CLASS,
524 METH_NOARGS | METH_CLASS,
417 "constructor to help legacy API to build a new \"from_p2\" item"},
525 "constructor to help legacy API to build a new \"from_p2\" item"},
418 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
526 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
419 METH_NOARGS | METH_CLASS,
527 METH_NOARGS | METH_CLASS,
420 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
528 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
421 {"new_normal", (PyCFunction)dirstate_item_new_normal,
529 {"new_normal", (PyCFunction)dirstate_item_new_normal,
422 METH_VARARGS | METH_CLASS,
530 METH_VARARGS | METH_CLASS,
423 "constructor to help legacy API to build a new \"normal\" item"},
531 "constructor to help legacy API to build a new \"normal\" item"},
424 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
532 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
425 METH_NOARGS, "mark a file as \"possibly dirty\""},
533 METH_NOARGS, "mark a file as \"possibly dirty\""},
426 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
534 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
427 "mark a file as \"untracked\""},
535 "mark a file as \"untracked\""},
428 {NULL} /* Sentinel */
536 {NULL} /* Sentinel */
429 };
537 };
430
538
431 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
539 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
432 {
540 {
433 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
541 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
434 };
542 };
435
543
436 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
544 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
437 {
545 {
438 return PyInt_FromLong(dirstate_item_c_v1_size(self));
546 return PyInt_FromLong(dirstate_item_c_v1_size(self));
439 };
547 };
440
548
441 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
549 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
442 {
550 {
443 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
551 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
444 };
552 };
445
553
446 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
554 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
447 {
555 {
448 char state = dirstate_item_c_v1_state(self);
556 char state = dirstate_item_c_v1_state(self);
449 return PyBytes_FromStringAndSize(&state, 1);
557 return PyBytes_FromStringAndSize(&state, 1);
450 };
558 };
451
559
452 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
560 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
453 {
561 {
454 if (dirstate_item_c_tracked(self)) {
562 if (dirstate_item_c_tracked(self)) {
455 Py_RETURN_TRUE;
563 Py_RETURN_TRUE;
456 } else {
564 } else {
457 Py_RETURN_FALSE;
565 Py_RETURN_FALSE;
458 }
566 }
459 };
567 };
460
568
461 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
569 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
462 {
570 {
463 if (dirstate_item_c_added(self)) {
571 if (dirstate_item_c_added(self)) {
464 Py_RETURN_TRUE;
572 Py_RETURN_TRUE;
465 } else {
573 } else {
466 Py_RETURN_FALSE;
574 Py_RETURN_FALSE;
467 }
575 }
468 };
576 };
469
577
470 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
578 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
471 {
579 {
472 if (dirstate_item_c_merged(self)) {
580 if (dirstate_item_c_merged(self)) {
473 Py_RETURN_TRUE;
581 Py_RETURN_TRUE;
474 } else {
582 } else {
475 Py_RETURN_FALSE;
583 Py_RETURN_FALSE;
476 }
584 }
477 };
585 };
478
586
479 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
587 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
480 {
588 {
481 if (dirstate_item_c_merged_removed(self)) {
589 if (dirstate_item_c_merged_removed(self)) {
482 Py_RETURN_TRUE;
590 Py_RETURN_TRUE;
483 } else {
591 } else {
484 Py_RETURN_FALSE;
592 Py_RETURN_FALSE;
485 }
593 }
486 };
594 };
487
595
488 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
596 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
489 {
597 {
490 if (dirstate_item_c_from_p2(self)) {
598 if (dirstate_item_c_from_p2(self)) {
491 Py_RETURN_TRUE;
599 Py_RETURN_TRUE;
492 } else {
600 } else {
493 Py_RETURN_FALSE;
601 Py_RETURN_FALSE;
494 }
602 }
495 };
603 };
496
604
497 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
605 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
498 {
606 {
499 if (dirstate_item_c_from_p2_removed(self)) {
607 if (dirstate_item_c_from_p2_removed(self)) {
500 Py_RETURN_TRUE;
608 Py_RETURN_TRUE;
501 } else {
609 } else {
502 Py_RETURN_FALSE;
610 Py_RETURN_FALSE;
503 }
611 }
504 };
612 };
505
613
506 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
614 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
507 {
615 {
508 if (dirstate_item_c_removed(self)) {
616 if (dirstate_item_c_removed(self)) {
509 Py_RETURN_TRUE;
617 Py_RETURN_TRUE;
510 } else {
618 } else {
511 Py_RETURN_FALSE;
619 Py_RETURN_FALSE;
512 }
620 }
513 };
621 };
514
622
515 static PyObject *dm_nonnormal(dirstateItemObject *self)
623 static PyObject *dm_nonnormal(dirstateItemObject *self)
516 {
624 {
517 if ((dirstate_item_c_v1_state(self) != 'n') ||
625 if ((dirstate_item_c_v1_state(self) != 'n') ||
518 (dirstate_item_c_v1_mtime(self) == ambiguous_time)) {
626 (dirstate_item_c_v1_mtime(self) == ambiguous_time)) {
519 Py_RETURN_TRUE;
627 Py_RETURN_TRUE;
520 } else {
628 } else {
521 Py_RETURN_FALSE;
629 Py_RETURN_FALSE;
522 }
630 }
523 };
631 };
524 static PyObject *dm_otherparent(dirstateItemObject *self)
632 static PyObject *dm_otherparent(dirstateItemObject *self)
525 {
633 {
526 if (dirstate_item_c_v1_mtime(self) == dirstate_v1_from_p2) {
634 if (dirstate_item_c_v1_mtime(self) == dirstate_v1_from_p2) {
527 Py_RETURN_TRUE;
635 Py_RETURN_TRUE;
528 } else {
636 } else {
529 Py_RETURN_FALSE;
637 Py_RETURN_FALSE;
530 }
638 }
531 };
639 };
532
640
533 static PyGetSetDef dirstate_item_getset[] = {
641 static PyGetSetDef dirstate_item_getset[] = {
534 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
642 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
535 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
643 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
536 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
644 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
537 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
645 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
538 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
646 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
539 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
647 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
540 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
648 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
541 "merged_removed", NULL},
649 "merged_removed", NULL},
542 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
650 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
543 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
651 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
544 "from_p2_removed", NULL},
652 "from_p2_removed", NULL},
545 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
653 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
546 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
654 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
547 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
655 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
548 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
656 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
549 {NULL} /* Sentinel */
657 {NULL} /* Sentinel */
550 };
658 };
551
659
552 PyTypeObject dirstateItemType = {
660 PyTypeObject dirstateItemType = {
553 PyVarObject_HEAD_INIT(NULL, 0) /* header */
661 PyVarObject_HEAD_INIT(NULL, 0) /* header */
554 "dirstate_tuple", /* tp_name */
662 "dirstate_tuple", /* tp_name */
555 sizeof(dirstateItemObject), /* tp_basicsize */
663 sizeof(dirstateItemObject), /* tp_basicsize */
556 0, /* tp_itemsize */
664 0, /* tp_itemsize */
557 (destructor)dirstate_item_dealloc, /* tp_dealloc */
665 (destructor)dirstate_item_dealloc, /* tp_dealloc */
558 0, /* tp_print */
666 0, /* tp_print */
559 0, /* tp_getattr */
667 0, /* tp_getattr */
560 0, /* tp_setattr */
668 0, /* tp_setattr */
561 0, /* tp_compare */
669 0, /* tp_compare */
562 0, /* tp_repr */
670 0, /* tp_repr */
563 0, /* tp_as_number */
671 0, /* tp_as_number */
564 0, /* tp_as_sequence */
672 0, /* tp_as_sequence */
565 0, /* tp_as_mapping */
673 0, /* tp_as_mapping */
566 0, /* tp_hash */
674 0, /* tp_hash */
567 0, /* tp_call */
675 0, /* tp_call */
568 0, /* tp_str */
676 0, /* tp_str */
569 0, /* tp_getattro */
677 0, /* tp_getattro */
570 0, /* tp_setattro */
678 0, /* tp_setattro */
571 0, /* tp_as_buffer */
679 0, /* tp_as_buffer */
572 Py_TPFLAGS_DEFAULT, /* tp_flags */
680 Py_TPFLAGS_DEFAULT, /* tp_flags */
573 "dirstate tuple", /* tp_doc */
681 "dirstate tuple", /* tp_doc */
574 0, /* tp_traverse */
682 0, /* tp_traverse */
575 0, /* tp_clear */
683 0, /* tp_clear */
576 0, /* tp_richcompare */
684 0, /* tp_richcompare */
577 0, /* tp_weaklistoffset */
685 0, /* tp_weaklistoffset */
578 0, /* tp_iter */
686 0, /* tp_iter */
579 0, /* tp_iternext */
687 0, /* tp_iternext */
580 dirstate_item_methods, /* tp_methods */
688 dirstate_item_methods, /* tp_methods */
581 0, /* tp_members */
689 0, /* tp_members */
582 dirstate_item_getset, /* tp_getset */
690 dirstate_item_getset, /* tp_getset */
583 0, /* tp_base */
691 0, /* tp_base */
584 0, /* tp_dict */
692 0, /* tp_dict */
585 0, /* tp_descr_get */
693 0, /* tp_descr_get */
586 0, /* tp_descr_set */
694 0, /* tp_descr_set */
587 0, /* tp_dictoffset */
695 0, /* tp_dictoffset */
588 0, /* tp_init */
696 0, /* tp_init */
589 0, /* tp_alloc */
697 0, /* tp_alloc */
590 dirstate_item_new, /* tp_new */
698 dirstate_item_new, /* tp_new */
591 };
699 };
592
700
593 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
701 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
594 {
702 {
595 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
703 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
596 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
704 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
597 char state, *cur, *str, *cpos;
705 char state, *cur, *str, *cpos;
598 int mode, size, mtime;
706 int mode, size, mtime;
599 unsigned int flen, pos = 40;
707 unsigned int flen, pos = 40;
600 Py_ssize_t len = 40;
708 Py_ssize_t len = 40;
601 Py_ssize_t readlen;
709 Py_ssize_t readlen;
602
710
603 if (!PyArg_ParseTuple(
711 if (!PyArg_ParseTuple(
604 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
712 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
605 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
713 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
606 goto quit;
714 goto quit;
607 }
715 }
608
716
609 len = readlen;
717 len = readlen;
610
718
611 /* read parents */
719 /* read parents */
612 if (len < 40) {
720 if (len < 40) {
613 PyErr_SetString(PyExc_ValueError,
721 PyErr_SetString(PyExc_ValueError,
614 "too little data for parents");
722 "too little data for parents");
615 goto quit;
723 goto quit;
616 }
724 }
617
725
618 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
726 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
619 str + 20, (Py_ssize_t)20);
727 str + 20, (Py_ssize_t)20);
620 if (!parents) {
728 if (!parents) {
621 goto quit;
729 goto quit;
622 }
730 }
623
731
624 /* read filenames */
732 /* read filenames */
625 while (pos >= 40 && pos < len) {
733 while (pos >= 40 && pos < len) {
626 if (pos + 17 > len) {
734 if (pos + 17 > len) {
627 PyErr_SetString(PyExc_ValueError,
735 PyErr_SetString(PyExc_ValueError,
628 "overflow in dirstate");
736 "overflow in dirstate");
629 goto quit;
737 goto quit;
630 }
738 }
631 cur = str + pos;
739 cur = str + pos;
632 /* unpack header */
740 /* unpack header */
633 state = *cur;
741 state = *cur;
634 mode = getbe32(cur + 1);
742 mode = getbe32(cur + 1);
635 size = getbe32(cur + 5);
743 size = getbe32(cur + 5);
636 mtime = getbe32(cur + 9);
744 mtime = getbe32(cur + 9);
637 flen = getbe32(cur + 13);
745 flen = getbe32(cur + 13);
638 pos += 17;
746 pos += 17;
639 cur += 17;
747 cur += 17;
640 if (flen > len - pos) {
748 if (flen > len - pos) {
641 PyErr_SetString(PyExc_ValueError,
749 PyErr_SetString(PyExc_ValueError,
642 "overflow in dirstate");
750 "overflow in dirstate");
643 goto quit;
751 goto quit;
644 }
752 }
645
753
646 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
754 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
647 size, mtime);
755 size, mtime);
648 cpos = memchr(cur, 0, flen);
756 cpos = memchr(cur, 0, flen);
649 if (cpos) {
757 if (cpos) {
650 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
758 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
651 cname = PyBytes_FromStringAndSize(
759 cname = PyBytes_FromStringAndSize(
652 cpos + 1, flen - (cpos - cur) - 1);
760 cpos + 1, flen - (cpos - cur) - 1);
653 if (!fname || !cname ||
761 if (!fname || !cname ||
654 PyDict_SetItem(cmap, fname, cname) == -1 ||
762 PyDict_SetItem(cmap, fname, cname) == -1 ||
655 PyDict_SetItem(dmap, fname, entry) == -1) {
763 PyDict_SetItem(dmap, fname, entry) == -1) {
656 goto quit;
764 goto quit;
657 }
765 }
658 Py_DECREF(cname);
766 Py_DECREF(cname);
659 } else {
767 } else {
660 fname = PyBytes_FromStringAndSize(cur, flen);
768 fname = PyBytes_FromStringAndSize(cur, flen);
661 if (!fname ||
769 if (!fname ||
662 PyDict_SetItem(dmap, fname, entry) == -1) {
770 PyDict_SetItem(dmap, fname, entry) == -1) {
663 goto quit;
771 goto quit;
664 }
772 }
665 }
773 }
666 Py_DECREF(fname);
774 Py_DECREF(fname);
667 Py_DECREF(entry);
775 Py_DECREF(entry);
668 fname = cname = entry = NULL;
776 fname = cname = entry = NULL;
669 pos += flen;
777 pos += flen;
670 }
778 }
671
779
672 ret = parents;
780 ret = parents;
673 Py_INCREF(ret);
781 Py_INCREF(ret);
674 quit:
782 quit:
675 Py_XDECREF(fname);
783 Py_XDECREF(fname);
676 Py_XDECREF(cname);
784 Py_XDECREF(cname);
677 Py_XDECREF(entry);
785 Py_XDECREF(entry);
678 Py_XDECREF(parents);
786 Py_XDECREF(parents);
679 return ret;
787 return ret;
680 }
788 }
681
789
682 /*
790 /*
683 * Build a set of non-normal and other parent entries from the dirstate dmap
791 * Build a set of non-normal and other parent entries from the dirstate dmap
684 */
792 */
685 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
793 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
686 {
794 {
687 PyObject *dmap, *fname, *v;
795 PyObject *dmap, *fname, *v;
688 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
796 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
689 Py_ssize_t pos;
797 Py_ssize_t pos;
690
798
691 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
799 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
692 &dmap)) {
800 &dmap)) {
693 goto bail;
801 goto bail;
694 }
802 }
695
803
696 nonnset = PySet_New(NULL);
804 nonnset = PySet_New(NULL);
697 if (nonnset == NULL) {
805 if (nonnset == NULL) {
698 goto bail;
806 goto bail;
699 }
807 }
700
808
701 otherpset = PySet_New(NULL);
809 otherpset = PySet_New(NULL);
702 if (otherpset == NULL) {
810 if (otherpset == NULL) {
703 goto bail;
811 goto bail;
704 }
812 }
705
813
706 pos = 0;
814 pos = 0;
707 while (PyDict_Next(dmap, &pos, &fname, &v)) {
815 while (PyDict_Next(dmap, &pos, &fname, &v)) {
708 dirstateItemObject *t;
816 dirstateItemObject *t;
709 if (!dirstate_tuple_check(v)) {
817 if (!dirstate_tuple_check(v)) {
710 PyErr_SetString(PyExc_TypeError,
818 PyErr_SetString(PyExc_TypeError,
711 "expected a dirstate tuple");
819 "expected a dirstate tuple");
712 goto bail;
820 goto bail;
713 }
821 }
714 t = (dirstateItemObject *)v;
822 t = (dirstateItemObject *)v;
715
823
716 if (t->state == 'n' && t->size == -2) {
824 if (dirstate_item_c_from_p2(t)) {
717 if (PySet_Add(otherpset, fname) == -1) {
825 if (PySet_Add(otherpset, fname) == -1) {
718 goto bail;
826 goto bail;
719 }
827 }
720 }
828 }
721
829 if (!(t->flags & dirstate_flag_wc_tracked) ||
722 if (t->state == 'n' && t->mtime != -1) {
830 !(t->flags &
723 continue;
831 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked)) ||
724 }
832 (t->flags &
725 if (PySet_Add(nonnset, fname) == -1) {
833 (dirstate_flag_possibly_dirty | dirstate_flag_merged))) {
726 goto bail;
834 if (PySet_Add(nonnset, fname) == -1) {
835 goto bail;
836 }
727 }
837 }
728 }
838 }
729
839
730 result = Py_BuildValue("(OO)", nonnset, otherpset);
840 result = Py_BuildValue("(OO)", nonnset, otherpset);
731 if (result == NULL) {
841 if (result == NULL) {
732 goto bail;
842 goto bail;
733 }
843 }
734 Py_DECREF(nonnset);
844 Py_DECREF(nonnset);
735 Py_DECREF(otherpset);
845 Py_DECREF(otherpset);
736 return result;
846 return result;
737 bail:
847 bail:
738 Py_XDECREF(nonnset);
848 Py_XDECREF(nonnset);
739 Py_XDECREF(otherpset);
849 Py_XDECREF(otherpset);
740 Py_XDECREF(result);
850 Py_XDECREF(result);
741 return NULL;
851 return NULL;
742 }
852 }
743
853
744 /*
854 /*
745 * Efficiently pack a dirstate object into its on-disk format.
855 * Efficiently pack a dirstate object into its on-disk format.
746 */
856 */
747 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
857 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
748 {
858 {
749 PyObject *packobj = NULL;
859 PyObject *packobj = NULL;
750 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
860 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
751 Py_ssize_t nbytes, pos, l;
861 Py_ssize_t nbytes, pos, l;
752 PyObject *k, *v = NULL, *pn;
862 PyObject *k, *v = NULL, *pn;
753 char *p, *s;
863 char *p, *s;
754 int now;
864 int now;
755
865
756 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
866 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
757 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
867 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
758 &now)) {
868 &now)) {
759 return NULL;
869 return NULL;
760 }
870 }
761
871
762 if (PyTuple_Size(pl) != 2) {
872 if (PyTuple_Size(pl) != 2) {
763 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
873 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
764 return NULL;
874 return NULL;
765 }
875 }
766
876
767 /* Figure out how much we need to allocate. */
877 /* Figure out how much we need to allocate. */
768 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
878 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
769 PyObject *c;
879 PyObject *c;
770 if (!PyBytes_Check(k)) {
880 if (!PyBytes_Check(k)) {
771 PyErr_SetString(PyExc_TypeError, "expected string key");
881 PyErr_SetString(PyExc_TypeError, "expected string key");
772 goto bail;
882 goto bail;
773 }
883 }
774 nbytes += PyBytes_GET_SIZE(k) + 17;
884 nbytes += PyBytes_GET_SIZE(k) + 17;
775 c = PyDict_GetItem(copymap, k);
885 c = PyDict_GetItem(copymap, k);
776 if (c) {
886 if (c) {
777 if (!PyBytes_Check(c)) {
887 if (!PyBytes_Check(c)) {
778 PyErr_SetString(PyExc_TypeError,
888 PyErr_SetString(PyExc_TypeError,
779 "expected string key");
889 "expected string key");
780 goto bail;
890 goto bail;
781 }
891 }
782 nbytes += PyBytes_GET_SIZE(c) + 1;
892 nbytes += PyBytes_GET_SIZE(c) + 1;
783 }
893 }
784 }
894 }
785
895
786 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
896 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
787 if (packobj == NULL) {
897 if (packobj == NULL) {
788 goto bail;
898 goto bail;
789 }
899 }
790
900
791 p = PyBytes_AS_STRING(packobj);
901 p = PyBytes_AS_STRING(packobj);
792
902
793 pn = PyTuple_GET_ITEM(pl, 0);
903 pn = PyTuple_GET_ITEM(pl, 0);
794 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
904 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
795 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
905 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
796 goto bail;
906 goto bail;
797 }
907 }
798 memcpy(p, s, l);
908 memcpy(p, s, l);
799 p += 20;
909 p += 20;
800 pn = PyTuple_GET_ITEM(pl, 1);
910 pn = PyTuple_GET_ITEM(pl, 1);
801 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
911 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
802 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
912 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
803 goto bail;
913 goto bail;
804 }
914 }
805 memcpy(p, s, l);
915 memcpy(p, s, l);
806 p += 20;
916 p += 20;
807
917
808 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
918 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
809 dirstateItemObject *tuple;
919 dirstateItemObject *tuple;
810 char state;
920 char state;
811 int mode, size, mtime;
921 int mode, size, mtime;
812 Py_ssize_t len, l;
922 Py_ssize_t len, l;
813 PyObject *o;
923 PyObject *o;
814 char *t;
924 char *t;
815
925
816 if (!dirstate_tuple_check(v)) {
926 if (!dirstate_tuple_check(v)) {
817 PyErr_SetString(PyExc_TypeError,
927 PyErr_SetString(PyExc_TypeError,
818 "expected a dirstate tuple");
928 "expected a dirstate tuple");
819 goto bail;
929 goto bail;
820 }
930 }
821 tuple = (dirstateItemObject *)v;
931 tuple = (dirstateItemObject *)v;
822
932
823 state = tuple->state;
933 state = dirstate_item_c_v1_state(tuple);
824 mode = tuple->mode;
934 mode = dirstate_item_c_v1_mode(tuple);
825 size = tuple->size;
935 size = dirstate_item_c_v1_size(tuple);
826 mtime = tuple->mtime;
936 mtime = dirstate_item_c_v1_mtime(tuple);
827 if (state == 'n' && mtime == now) {
937 if (state == 'n' && mtime == now) {
828 /* See pure/parsers.py:pack_dirstate for why we do
938 /* See pure/parsers.py:pack_dirstate for why we do
829 * this. */
939 * this. */
830 mtime = -1;
940 mtime = -1;
831 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
941 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
832 state, mode, size, mtime);
942 state, mode, size, mtime);
833 if (!mtime_unset) {
943 if (!mtime_unset) {
834 goto bail;
944 goto bail;
835 }
945 }
836 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
946 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
837 goto bail;
947 goto bail;
838 }
948 }
839 Py_DECREF(mtime_unset);
949 Py_DECREF(mtime_unset);
840 mtime_unset = NULL;
950 mtime_unset = NULL;
841 }
951 }
842 *p++ = state;
952 *p++ = state;
843 putbe32((uint32_t)mode, p);
953 putbe32((uint32_t)mode, p);
844 putbe32((uint32_t)size, p + 4);
954 putbe32((uint32_t)size, p + 4);
845 putbe32((uint32_t)mtime, p + 8);
955 putbe32((uint32_t)mtime, p + 8);
846 t = p + 12;
956 t = p + 12;
847 p += 16;
957 p += 16;
848 len = PyBytes_GET_SIZE(k);
958 len = PyBytes_GET_SIZE(k);
849 memcpy(p, PyBytes_AS_STRING(k), len);
959 memcpy(p, PyBytes_AS_STRING(k), len);
850 p += len;
960 p += len;
851 o = PyDict_GetItem(copymap, k);
961 o = PyDict_GetItem(copymap, k);
852 if (o) {
962 if (o) {
853 *p++ = '\0';
963 *p++ = '\0';
854 l = PyBytes_GET_SIZE(o);
964 l = PyBytes_GET_SIZE(o);
855 memcpy(p, PyBytes_AS_STRING(o), l);
965 memcpy(p, PyBytes_AS_STRING(o), l);
856 p += l;
966 p += l;
857 len += l + 1;
967 len += l + 1;
858 }
968 }
859 putbe32((uint32_t)len, t);
969 putbe32((uint32_t)len, t);
860 }
970 }
861
971
862 pos = p - PyBytes_AS_STRING(packobj);
972 pos = p - PyBytes_AS_STRING(packobj);
863 if (pos != nbytes) {
973 if (pos != nbytes) {
864 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
974 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
865 (long)pos, (long)nbytes);
975 (long)pos, (long)nbytes);
866 goto bail;
976 goto bail;
867 }
977 }
868
978
869 return packobj;
979 return packobj;
870 bail:
980 bail:
871 Py_XDECREF(mtime_unset);
981 Py_XDECREF(mtime_unset);
872 Py_XDECREF(packobj);
982 Py_XDECREF(packobj);
873 Py_XDECREF(v);
983 Py_XDECREF(v);
874 return NULL;
984 return NULL;
875 }
985 }
876
986
877 #define BUMPED_FIX 1
987 #define BUMPED_FIX 1
878 #define USING_SHA_256 2
988 #define USING_SHA_256 2
879 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
989 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
880
990
881 static PyObject *readshas(const char *source, unsigned char num,
991 static PyObject *readshas(const char *source, unsigned char num,
882 Py_ssize_t hashwidth)
992 Py_ssize_t hashwidth)
883 {
993 {
884 int i;
994 int i;
885 PyObject *list = PyTuple_New(num);
995 PyObject *list = PyTuple_New(num);
886 if (list == NULL) {
996 if (list == NULL) {
887 return NULL;
997 return NULL;
888 }
998 }
889 for (i = 0; i < num; i++) {
999 for (i = 0; i < num; i++) {
890 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
1000 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
891 if (hash == NULL) {
1001 if (hash == NULL) {
892 Py_DECREF(list);
1002 Py_DECREF(list);
893 return NULL;
1003 return NULL;
894 }
1004 }
895 PyTuple_SET_ITEM(list, i, hash);
1005 PyTuple_SET_ITEM(list, i, hash);
896 source += hashwidth;
1006 source += hashwidth;
897 }
1007 }
898 return list;
1008 return list;
899 }
1009 }
900
1010
901 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
1011 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
902 uint32_t *msize)
1012 uint32_t *msize)
903 {
1013 {
904 const char *data = databegin;
1014 const char *data = databegin;
905 const char *meta;
1015 const char *meta;
906
1016
907 double mtime;
1017 double mtime;
908 int16_t tz;
1018 int16_t tz;
909 uint16_t flags;
1019 uint16_t flags;
910 unsigned char nsuccs, nparents, nmetadata;
1020 unsigned char nsuccs, nparents, nmetadata;
911 Py_ssize_t hashwidth = 20;
1021 Py_ssize_t hashwidth = 20;
912
1022
913 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
1023 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
914 PyObject *metadata = NULL, *ret = NULL;
1024 PyObject *metadata = NULL, *ret = NULL;
915 int i;
1025 int i;
916
1026
917 if (data + FM1_HEADER_SIZE > dataend) {
1027 if (data + FM1_HEADER_SIZE > dataend) {
918 goto overflow;
1028 goto overflow;
919 }
1029 }
920
1030
921 *msize = getbe32(data);
1031 *msize = getbe32(data);
922 data += 4;
1032 data += 4;
923 mtime = getbefloat64(data);
1033 mtime = getbefloat64(data);
924 data += 8;
1034 data += 8;
925 tz = getbeint16(data);
1035 tz = getbeint16(data);
926 data += 2;
1036 data += 2;
927 flags = getbeuint16(data);
1037 flags = getbeuint16(data);
928 data += 2;
1038 data += 2;
929
1039
930 if (flags & USING_SHA_256) {
1040 if (flags & USING_SHA_256) {
931 hashwidth = 32;
1041 hashwidth = 32;
932 }
1042 }
933
1043
934 nsuccs = (unsigned char)(*data++);
1044 nsuccs = (unsigned char)(*data++);
935 nparents = (unsigned char)(*data++);
1045 nparents = (unsigned char)(*data++);
936 nmetadata = (unsigned char)(*data++);
1046 nmetadata = (unsigned char)(*data++);
937
1047
938 if (databegin + *msize > dataend) {
1048 if (databegin + *msize > dataend) {
939 goto overflow;
1049 goto overflow;
940 }
1050 }
941 dataend = databegin + *msize; /* narrow down to marker size */
1051 dataend = databegin + *msize; /* narrow down to marker size */
942
1052
943 if (data + hashwidth > dataend) {
1053 if (data + hashwidth > dataend) {
944 goto overflow;
1054 goto overflow;
945 }
1055 }
946 prec = PyBytes_FromStringAndSize(data, hashwidth);
1056 prec = PyBytes_FromStringAndSize(data, hashwidth);
947 data += hashwidth;
1057 data += hashwidth;
948 if (prec == NULL) {
1058 if (prec == NULL) {
949 goto bail;
1059 goto bail;
950 }
1060 }
951
1061
952 if (data + nsuccs * hashwidth > dataend) {
1062 if (data + nsuccs * hashwidth > dataend) {
953 goto overflow;
1063 goto overflow;
954 }
1064 }
955 succs = readshas(data, nsuccs, hashwidth);
1065 succs = readshas(data, nsuccs, hashwidth);
956 if (succs == NULL) {
1066 if (succs == NULL) {
957 goto bail;
1067 goto bail;
958 }
1068 }
959 data += nsuccs * hashwidth;
1069 data += nsuccs * hashwidth;
960
1070
961 if (nparents == 1 || nparents == 2) {
1071 if (nparents == 1 || nparents == 2) {
962 if (data + nparents * hashwidth > dataend) {
1072 if (data + nparents * hashwidth > dataend) {
963 goto overflow;
1073 goto overflow;
964 }
1074 }
965 parents = readshas(data, nparents, hashwidth);
1075 parents = readshas(data, nparents, hashwidth);
966 if (parents == NULL) {
1076 if (parents == NULL) {
967 goto bail;
1077 goto bail;
968 }
1078 }
969 data += nparents * hashwidth;
1079 data += nparents * hashwidth;
970 } else {
1080 } else {
971 parents = Py_None;
1081 parents = Py_None;
972 Py_INCREF(parents);
1082 Py_INCREF(parents);
973 }
1083 }
974
1084
975 if (data + 2 * nmetadata > dataend) {
1085 if (data + 2 * nmetadata > dataend) {
976 goto overflow;
1086 goto overflow;
977 }
1087 }
978 meta = data + (2 * nmetadata);
1088 meta = data + (2 * nmetadata);
979 metadata = PyTuple_New(nmetadata);
1089 metadata = PyTuple_New(nmetadata);
980 if (metadata == NULL) {
1090 if (metadata == NULL) {
981 goto bail;
1091 goto bail;
982 }
1092 }
983 for (i = 0; i < nmetadata; i++) {
1093 for (i = 0; i < nmetadata; i++) {
984 PyObject *tmp, *left = NULL, *right = NULL;
1094 PyObject *tmp, *left = NULL, *right = NULL;
985 Py_ssize_t leftsize = (unsigned char)(*data++);
1095 Py_ssize_t leftsize = (unsigned char)(*data++);
986 Py_ssize_t rightsize = (unsigned char)(*data++);
1096 Py_ssize_t rightsize = (unsigned char)(*data++);
987 if (meta + leftsize + rightsize > dataend) {
1097 if (meta + leftsize + rightsize > dataend) {
988 goto overflow;
1098 goto overflow;
989 }
1099 }
990 left = PyBytes_FromStringAndSize(meta, leftsize);
1100 left = PyBytes_FromStringAndSize(meta, leftsize);
991 meta += leftsize;
1101 meta += leftsize;
992 right = PyBytes_FromStringAndSize(meta, rightsize);
1102 right = PyBytes_FromStringAndSize(meta, rightsize);
993 meta += rightsize;
1103 meta += rightsize;
994 tmp = PyTuple_New(2);
1104 tmp = PyTuple_New(2);
995 if (!left || !right || !tmp) {
1105 if (!left || !right || !tmp) {
996 Py_XDECREF(left);
1106 Py_XDECREF(left);
997 Py_XDECREF(right);
1107 Py_XDECREF(right);
998 Py_XDECREF(tmp);
1108 Py_XDECREF(tmp);
999 goto bail;
1109 goto bail;
1000 }
1110 }
1001 PyTuple_SET_ITEM(tmp, 0, left);
1111 PyTuple_SET_ITEM(tmp, 0, left);
1002 PyTuple_SET_ITEM(tmp, 1, right);
1112 PyTuple_SET_ITEM(tmp, 1, right);
1003 PyTuple_SET_ITEM(metadata, i, tmp);
1113 PyTuple_SET_ITEM(metadata, i, tmp);
1004 }
1114 }
1005 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1115 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1006 (int)tz * 60, parents);
1116 (int)tz * 60, parents);
1007 goto bail; /* return successfully */
1117 goto bail; /* return successfully */
1008
1118
1009 overflow:
1119 overflow:
1010 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1120 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1011 bail:
1121 bail:
1012 Py_XDECREF(prec);
1122 Py_XDECREF(prec);
1013 Py_XDECREF(succs);
1123 Py_XDECREF(succs);
1014 Py_XDECREF(metadata);
1124 Py_XDECREF(metadata);
1015 Py_XDECREF(parents);
1125 Py_XDECREF(parents);
1016 return ret;
1126 return ret;
1017 }
1127 }
1018
1128
1019 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1129 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1020 {
1130 {
1021 const char *data, *dataend;
1131 const char *data, *dataend;
1022 Py_ssize_t datalen, offset, stop;
1132 Py_ssize_t datalen, offset, stop;
1023 PyObject *markers = NULL;
1133 PyObject *markers = NULL;
1024
1134
1025 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1135 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1026 &offset, &stop)) {
1136 &offset, &stop)) {
1027 return NULL;
1137 return NULL;
1028 }
1138 }
1029 if (offset < 0) {
1139 if (offset < 0) {
1030 PyErr_SetString(PyExc_ValueError,
1140 PyErr_SetString(PyExc_ValueError,
1031 "invalid negative offset in fm1readmarkers");
1141 "invalid negative offset in fm1readmarkers");
1032 return NULL;
1142 return NULL;
1033 }
1143 }
1034 if (stop > datalen) {
1144 if (stop > datalen) {
1035 PyErr_SetString(
1145 PyErr_SetString(
1036 PyExc_ValueError,
1146 PyExc_ValueError,
1037 "stop longer than data length in fm1readmarkers");
1147 "stop longer than data length in fm1readmarkers");
1038 return NULL;
1148 return NULL;
1039 }
1149 }
1040 dataend = data + datalen;
1150 dataend = data + datalen;
1041 data += offset;
1151 data += offset;
1042 markers = PyList_New(0);
1152 markers = PyList_New(0);
1043 if (!markers) {
1153 if (!markers) {
1044 return NULL;
1154 return NULL;
1045 }
1155 }
1046 while (offset < stop) {
1156 while (offset < stop) {
1047 uint32_t msize;
1157 uint32_t msize;
1048 int error;
1158 int error;
1049 PyObject *record = fm1readmarker(data, dataend, &msize);
1159 PyObject *record = fm1readmarker(data, dataend, &msize);
1050 if (!record) {
1160 if (!record) {
1051 goto bail;
1161 goto bail;
1052 }
1162 }
1053 error = PyList_Append(markers, record);
1163 error = PyList_Append(markers, record);
1054 Py_DECREF(record);
1164 Py_DECREF(record);
1055 if (error) {
1165 if (error) {
1056 goto bail;
1166 goto bail;
1057 }
1167 }
1058 data += msize;
1168 data += msize;
1059 offset += msize;
1169 offset += msize;
1060 }
1170 }
1061 return markers;
1171 return markers;
1062 bail:
1172 bail:
1063 Py_DECREF(markers);
1173 Py_DECREF(markers);
1064 return NULL;
1174 return NULL;
1065 }
1175 }
1066
1176
1067 static char parsers_doc[] = "Efficient content parsing.";
1177 static char parsers_doc[] = "Efficient content parsing.";
1068
1178
1069 PyObject *encodedir(PyObject *self, PyObject *args);
1179 PyObject *encodedir(PyObject *self, PyObject *args);
1070 PyObject *pathencode(PyObject *self, PyObject *args);
1180 PyObject *pathencode(PyObject *self, PyObject *args);
1071 PyObject *lowerencode(PyObject *self, PyObject *args);
1181 PyObject *lowerencode(PyObject *self, PyObject *args);
1072 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1182 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1073
1183
1074 static PyMethodDef methods[] = {
1184 static PyMethodDef methods[] = {
1075 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1185 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1076 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
1186 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
1077 "create a set containing non-normal and other parent entries of given "
1187 "create a set containing non-normal and other parent entries of given "
1078 "dirstate\n"},
1188 "dirstate\n"},
1079 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1189 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1080 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1190 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1081 "parse a revlog index\n"},
1191 "parse a revlog index\n"},
1082 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1192 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1083 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1193 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1084 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1194 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1085 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1195 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1086 "construct a dict with an expected size\n"},
1196 "construct a dict with an expected size\n"},
1087 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1197 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1088 "make file foldmap\n"},
1198 "make file foldmap\n"},
1089 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1199 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1090 "escape a UTF-8 byte string to JSON (fast path)\n"},
1200 "escape a UTF-8 byte string to JSON (fast path)\n"},
1091 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1201 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1092 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1202 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1093 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1203 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1094 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1204 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1095 "parse v1 obsolete markers\n"},
1205 "parse v1 obsolete markers\n"},
1096 {NULL, NULL}};
1206 {NULL, NULL}};
1097
1207
1098 void dirs_module_init(PyObject *mod);
1208 void dirs_module_init(PyObject *mod);
1099 void manifest_module_init(PyObject *mod);
1209 void manifest_module_init(PyObject *mod);
1100 void revlog_module_init(PyObject *mod);
1210 void revlog_module_init(PyObject *mod);
1101
1211
1102 static const int version = 20;
1212 static const int version = 20;
1103
1213
1104 static void module_init(PyObject *mod)
1214 static void module_init(PyObject *mod)
1105 {
1215 {
1106 PyObject *capsule = NULL;
1216 PyObject *capsule = NULL;
1107 PyModule_AddIntConstant(mod, "version", version);
1217 PyModule_AddIntConstant(mod, "version", version);
1108
1218
1109 /* This module constant has two purposes. First, it lets us unit test
1219 /* This module constant has two purposes. First, it lets us unit test
1110 * the ImportError raised without hard-coding any error text. This
1220 * the ImportError raised without hard-coding any error text. This
1111 * means we can change the text in the future without breaking tests,
1221 * means we can change the text in the future without breaking tests,
1112 * even across changesets without a recompile. Second, its presence
1222 * even across changesets without a recompile. Second, its presence
1113 * can be used to determine whether the version-checking logic is
1223 * can be used to determine whether the version-checking logic is
1114 * present, which also helps in testing across changesets without a
1224 * present, which also helps in testing across changesets without a
1115 * recompile. Note that this means the pure-Python version of parsers
1225 * recompile. Note that this means the pure-Python version of parsers
1116 * should not have this module constant. */
1226 * should not have this module constant. */
1117 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1227 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1118
1228
1119 dirs_module_init(mod);
1229 dirs_module_init(mod);
1120 manifest_module_init(mod);
1230 manifest_module_init(mod);
1121 revlog_module_init(mod);
1231 revlog_module_init(mod);
1122
1232
1123 capsule = PyCapsule_New(
1233 capsule = PyCapsule_New(
1124 dirstate_item_from_v1_data,
1234 dirstate_item_from_v1_data,
1125 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
1235 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
1126 if (capsule != NULL)
1236 if (capsule != NULL)
1127 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
1237 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
1128
1238
1129 if (PyType_Ready(&dirstateItemType) < 0) {
1239 if (PyType_Ready(&dirstateItemType) < 0) {
1130 return;
1240 return;
1131 }
1241 }
1132 Py_INCREF(&dirstateItemType);
1242 Py_INCREF(&dirstateItemType);
1133 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1243 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1134 }
1244 }
1135
1245
1136 static int check_python_version(void)
1246 static int check_python_version(void)
1137 {
1247 {
1138 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1248 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1139 long hexversion;
1249 long hexversion;
1140 if (!sys) {
1250 if (!sys) {
1141 return -1;
1251 return -1;
1142 }
1252 }
1143 ver = PyObject_GetAttrString(sys, "hexversion");
1253 ver = PyObject_GetAttrString(sys, "hexversion");
1144 Py_DECREF(sys);
1254 Py_DECREF(sys);
1145 if (!ver) {
1255 if (!ver) {
1146 return -1;
1256 return -1;
1147 }
1257 }
1148 hexversion = PyInt_AsLong(ver);
1258 hexversion = PyInt_AsLong(ver);
1149 Py_DECREF(ver);
1259 Py_DECREF(ver);
1150 /* sys.hexversion is a 32-bit number by default, so the -1 case
1260 /* sys.hexversion is a 32-bit number by default, so the -1 case
1151 * should only occur in unusual circumstances (e.g. if sys.hexversion
1261 * should only occur in unusual circumstances (e.g. if sys.hexversion
1152 * is manually set to an invalid value). */
1262 * is manually set to an invalid value). */
1153 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1263 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1154 PyErr_Format(PyExc_ImportError,
1264 PyErr_Format(PyExc_ImportError,
1155 "%s: The Mercurial extension "
1265 "%s: The Mercurial extension "
1156 "modules were compiled with Python " PY_VERSION
1266 "modules were compiled with Python " PY_VERSION
1157 ", but "
1267 ", but "
1158 "Mercurial is currently using Python with "
1268 "Mercurial is currently using Python with "
1159 "sys.hexversion=%ld: "
1269 "sys.hexversion=%ld: "
1160 "Python %s\n at: %s",
1270 "Python %s\n at: %s",
1161 versionerrortext, hexversion, Py_GetVersion(),
1271 versionerrortext, hexversion, Py_GetVersion(),
1162 Py_GetProgramFullPath());
1272 Py_GetProgramFullPath());
1163 return -1;
1273 return -1;
1164 }
1274 }
1165 return 0;
1275 return 0;
1166 }
1276 }
1167
1277
1168 #ifdef IS_PY3K
1278 #ifdef IS_PY3K
1169 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1279 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1170 parsers_doc, -1, methods};
1280 parsers_doc, -1, methods};
1171
1281
1172 PyMODINIT_FUNC PyInit_parsers(void)
1282 PyMODINIT_FUNC PyInit_parsers(void)
1173 {
1283 {
1174 PyObject *mod;
1284 PyObject *mod;
1175
1285
1176 if (check_python_version() == -1)
1286 if (check_python_version() == -1)
1177 return NULL;
1287 return NULL;
1178 mod = PyModule_Create(&parsers_module);
1288 mod = PyModule_Create(&parsers_module);
1179 module_init(mod);
1289 module_init(mod);
1180 return mod;
1290 return mod;
1181 }
1291 }
1182 #else
1292 #else
1183 PyMODINIT_FUNC initparsers(void)
1293 PyMODINIT_FUNC initparsers(void)
1184 {
1294 {
1185 PyObject *mod;
1295 PyObject *mod;
1186
1296
1187 if (check_python_version() == -1) {
1297 if (check_python_version() == -1) {
1188 return;
1298 return;
1189 }
1299 }
1190 mod = Py_InitModule3("parsers", methods, parsers_doc);
1300 mod = Py_InitModule3("parsers", methods, parsers_doc);
1191 module_init(mod);
1301 module_init(mod);
1192 }
1302 }
1193 #endif
1303 #endif
@@ -1,74 +1,83 b''
1 /*
1 /*
2 util.h - utility functions for interfacing with the various python APIs.
2 util.h - utility functions for interfacing with the various python APIs.
3
3
4 This software may be used and distributed according to the terms of
4 This software may be used and distributed according to the terms of
5 the GNU General Public License, incorporated herein by reference.
5 the GNU General Public License, incorporated herein by reference.
6 */
6 */
7
7
8 #ifndef _HG_UTIL_H_
8 #ifndef _HG_UTIL_H_
9 #define _HG_UTIL_H_
9 #define _HG_UTIL_H_
10
10
11 #include "compat.h"
11 #include "compat.h"
12
12
13 #if PY_MAJOR_VERSION >= 3
13 #if PY_MAJOR_VERSION >= 3
14 #define IS_PY3K
14 #define IS_PY3K
15 #endif
15 #endif
16
16
17 /* helper to switch things like string literal depending on Python version */
17 /* helper to switch things like string literal depending on Python version */
18 #ifdef IS_PY3K
18 #ifdef IS_PY3K
19 #define PY23(py2, py3) py3
19 #define PY23(py2, py3) py3
20 #else
20 #else
21 #define PY23(py2, py3) py2
21 #define PY23(py2, py3) py2
22 #endif
22 #endif
23
23
24 /* clang-format off */
24 /* clang-format off */
25 typedef struct {
25 typedef struct {
26 PyObject_HEAD
26 PyObject_HEAD
27 char state;
27 char flags;
28 int mode;
28 int mode;
29 int size;
29 int size;
30 int mtime;
30 int mtime;
31 } dirstateItemObject;
31 } dirstateItemObject;
32 /* clang-format on */
32 /* clang-format on */
33
33
34 static const char dirstate_flag_wc_tracked = 1;
35 static const char dirstate_flag_p1_tracked = 1 << 1;
36 static const char dirstate_flag_p2_tracked = 1 << 2;
37 static const char dirstate_flag_possibly_dirty = 1 << 3;
38 static const char dirstate_flag_merged = 1 << 4;
39 static const char dirstate_flag_clean_p1 = 1 << 5;
40 static const char dirstate_flag_clean_p2 = 1 << 6;
41 static const char dirstate_flag_rust_special = 1 << 7;
42
34 extern PyTypeObject dirstateItemType;
43 extern PyTypeObject dirstateItemType;
35 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType)
44 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType)
36
45
37 #ifndef MIN
46 #ifndef MIN
38 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
47 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
39 #endif
48 #endif
40 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
49 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
41 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
50 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
42 #define true 1
51 #define true 1
43 #define false 0
52 #define false 0
44 typedef unsigned char bool;
53 typedef unsigned char bool;
45 #else
54 #else
46 #include <stdbool.h>
55 #include <stdbool.h>
47 #endif
56 #endif
48
57
49 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
58 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
50 {
59 {
51 /* _PyDict_NewPresized expects a minused parameter, but it actually
60 /* _PyDict_NewPresized expects a minused parameter, but it actually
52 creates a dictionary that's the nearest power of two bigger than the
61 creates a dictionary that's the nearest power of two bigger than the
53 parameter. For example, with the initial minused = 1000, the
62 parameter. For example, with the initial minused = 1000, the
54 dictionary created has size 1024. Of course in a lot of cases that
63 dictionary created has size 1024. Of course in a lot of cases that
55 can be greater than the maximum load factor Python's dict object
64 can be greater than the maximum load factor Python's dict object
56 expects (= 2/3), so as soon as we cross the threshold we'll resize
65 expects (= 2/3), so as soon as we cross the threshold we'll resize
57 anyway. So create a dictionary that's at least 3/2 the size. */
66 anyway. So create a dictionary that's at least 3/2 the size. */
58 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
67 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
59 }
68 }
60
69
61 /* Convert a PyInt or PyLong to a long. Returns false if there is an
70 /* Convert a PyInt or PyLong to a long. Returns false if there is an
62 error, in which case an exception will already have been set. */
71 error, in which case an exception will already have been set. */
63 static inline bool pylong_to_long(PyObject *pylong, long *out)
72 static inline bool pylong_to_long(PyObject *pylong, long *out)
64 {
73 {
65 *out = PyLong_AsLong(pylong);
74 *out = PyLong_AsLong(pylong);
66 /* Fast path to avoid hitting PyErr_Occurred if the value was obviously
75 /* Fast path to avoid hitting PyErr_Occurred if the value was obviously
67 * not an error. */
76 * not an error. */
68 if (*out != -1) {
77 if (*out != -1) {
69 return true;
78 return true;
70 }
79 }
71 return PyErr_Occurred() == NULL;
80 return PyErr_Occurred() == NULL;
72 }
81 }
73
82
74 #endif /* _HG_UTIL_H_ */
83 #endif /* _HG_UTIL_H_ */
General Comments 0
You need to be logged in to leave comments. Login now