##// END OF EJS Templates
cext: factor out header for charencode.c...
Yuya Nishihara -
r33753:0f4ac3b6 default
parent child Browse files
Show More
@@ -1,209 +1,210 b''
1 /*
1 /*
2 charencode.c - miscellaneous character encoding
2 charencode.c - miscellaneous character encoding
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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 #include <Python.h>
10 #include <Python.h>
11
11
12 #include "charencode.h"
12 #include "util.h"
13 #include "util.h"
13
14
14 static const char lowertable[128] = {
15 static const char lowertable[128] = {
15 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
16 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
16 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
17 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
17 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
18 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
18 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
19 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
19 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
20 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
20 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
21 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
21 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
22 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
22 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
23 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
23 '\x40',
24 '\x40',
24 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
25 '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', /* A-G */
25 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
26 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f', /* H-O */
26 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
27 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', /* P-W */
27 '\x78', '\x79', '\x7a', /* X-Z */
28 '\x78', '\x79', '\x7a', /* X-Z */
28 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
29 '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
29 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
30 '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67',
30 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
31 '\x68', '\x69', '\x6a', '\x6b', '\x6c', '\x6d', '\x6e', '\x6f',
31 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
32 '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77',
32 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
33 '\x78', '\x79', '\x7a', '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
33 };
34 };
34
35
35 static const char uppertable[128] = {
36 static const char uppertable[128] = {
36 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
37 '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
37 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
38 '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
38 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
39 '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
39 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
40 '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
40 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
41 '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27',
41 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
42 '\x28', '\x29', '\x2a', '\x2b', '\x2c', '\x2d', '\x2e', '\x2f',
42 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
43 '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37',
43 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
44 '\x38', '\x39', '\x3a', '\x3b', '\x3c', '\x3d', '\x3e', '\x3f',
44 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
45 '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47',
45 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
46 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f',
46 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
47 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57',
47 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
48 '\x58', '\x59', '\x5a', '\x5b', '\x5c', '\x5d', '\x5e', '\x5f',
48 '\x60',
49 '\x60',
49 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
50 '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', /* a-g */
50 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
51 '\x48', '\x49', '\x4a', '\x4b', '\x4c', '\x4d', '\x4e', '\x4f', /* h-o */
51 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
52 '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', /* p-w */
52 '\x58', '\x59', '\x5a', /* x-z */
53 '\x58', '\x59', '\x5a', /* x-z */
53 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
54 '\x7b', '\x7c', '\x7d', '\x7e', '\x7f'
54 };
55 };
55
56
56 /*
57 /*
57 * Turn a hex-encoded string into binary.
58 * Turn a hex-encoded string into binary.
58 */
59 */
59 PyObject *unhexlify(const char *str, int len)
60 PyObject *unhexlify(const char *str, int len)
60 {
61 {
61 PyObject *ret;
62 PyObject *ret;
62 char *d;
63 char *d;
63 int i;
64 int i;
64
65
65 ret = PyBytes_FromStringAndSize(NULL, len / 2);
66 ret = PyBytes_FromStringAndSize(NULL, len / 2);
66
67
67 if (!ret)
68 if (!ret)
68 return NULL;
69 return NULL;
69
70
70 d = PyBytes_AsString(ret);
71 d = PyBytes_AsString(ret);
71
72
72 for (i = 0; i < len;) {
73 for (i = 0; i < len;) {
73 int hi = hexdigit(str, i++);
74 int hi = hexdigit(str, i++);
74 int lo = hexdigit(str, i++);
75 int lo = hexdigit(str, i++);
75 *d++ = (hi << 4) | lo;
76 *d++ = (hi << 4) | lo;
76 }
77 }
77
78
78 return ret;
79 return ret;
79 }
80 }
80
81
81 static inline PyObject *_asciitransform(PyObject *str_obj,
82 static inline PyObject *_asciitransform(PyObject *str_obj,
82 const char table[128],
83 const char table[128],
83 PyObject *fallback_fn)
84 PyObject *fallback_fn)
84 {
85 {
85 char *str, *newstr;
86 char *str, *newstr;
86 Py_ssize_t i, len;
87 Py_ssize_t i, len;
87 PyObject *newobj = NULL;
88 PyObject *newobj = NULL;
88 PyObject *ret = NULL;
89 PyObject *ret = NULL;
89
90
90 str = PyBytes_AS_STRING(str_obj);
91 str = PyBytes_AS_STRING(str_obj);
91 len = PyBytes_GET_SIZE(str_obj);
92 len = PyBytes_GET_SIZE(str_obj);
92
93
93 newobj = PyBytes_FromStringAndSize(NULL, len);
94 newobj = PyBytes_FromStringAndSize(NULL, len);
94 if (!newobj)
95 if (!newobj)
95 goto quit;
96 goto quit;
96
97
97 newstr = PyBytes_AS_STRING(newobj);
98 newstr = PyBytes_AS_STRING(newobj);
98
99
99 for (i = 0; i < len; i++) {
100 for (i = 0; i < len; i++) {
100 char c = str[i];
101 char c = str[i];
101 if (c & 0x80) {
102 if (c & 0x80) {
102 if (fallback_fn != NULL) {
103 if (fallback_fn != NULL) {
103 ret = PyObject_CallFunctionObjArgs(fallback_fn,
104 ret = PyObject_CallFunctionObjArgs(fallback_fn,
104 str_obj, NULL);
105 str_obj, NULL);
105 } else {
106 } else {
106 PyObject *err = PyUnicodeDecodeError_Create(
107 PyObject *err = PyUnicodeDecodeError_Create(
107 "ascii", str, len, i, (i + 1),
108 "ascii", str, len, i, (i + 1),
108 "unexpected code byte");
109 "unexpected code byte");
109 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
110 PyErr_SetObject(PyExc_UnicodeDecodeError, err);
110 Py_XDECREF(err);
111 Py_XDECREF(err);
111 }
112 }
112 goto quit;
113 goto quit;
113 }
114 }
114 newstr[i] = table[(unsigned char)c];
115 newstr[i] = table[(unsigned char)c];
115 }
116 }
116
117
117 ret = newobj;
118 ret = newobj;
118 Py_INCREF(ret);
119 Py_INCREF(ret);
119 quit:
120 quit:
120 Py_XDECREF(newobj);
121 Py_XDECREF(newobj);
121 return ret;
122 return ret;
122 }
123 }
123
124
124 PyObject *asciilower(PyObject *self, PyObject *args)
125 PyObject *asciilower(PyObject *self, PyObject *args)
125 {
126 {
126 PyObject *str_obj;
127 PyObject *str_obj;
127 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
128 if (!PyArg_ParseTuple(args, "O!:asciilower", &PyBytes_Type, &str_obj))
128 return NULL;
129 return NULL;
129 return _asciitransform(str_obj, lowertable, NULL);
130 return _asciitransform(str_obj, lowertable, NULL);
130 }
131 }
131
132
132 PyObject *asciiupper(PyObject *self, PyObject *args)
133 PyObject *asciiupper(PyObject *self, PyObject *args)
133 {
134 {
134 PyObject *str_obj;
135 PyObject *str_obj;
135 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
136 if (!PyArg_ParseTuple(args, "O!:asciiupper", &PyBytes_Type, &str_obj))
136 return NULL;
137 return NULL;
137 return _asciitransform(str_obj, uppertable, NULL);
138 return _asciitransform(str_obj, uppertable, NULL);
138 }
139 }
139
140
140 PyObject *make_file_foldmap(PyObject *self, PyObject *args)
141 PyObject *make_file_foldmap(PyObject *self, PyObject *args)
141 {
142 {
142 PyObject *dmap, *spec_obj, *normcase_fallback;
143 PyObject *dmap, *spec_obj, *normcase_fallback;
143 PyObject *file_foldmap = NULL;
144 PyObject *file_foldmap = NULL;
144 enum normcase_spec spec;
145 enum normcase_spec spec;
145 PyObject *k, *v;
146 PyObject *k, *v;
146 dirstateTupleObject *tuple;
147 dirstateTupleObject *tuple;
147 Py_ssize_t pos = 0;
148 Py_ssize_t pos = 0;
148 const char *table;
149 const char *table;
149
150
150 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
151 if (!PyArg_ParseTuple(args, "O!O!O!:make_file_foldmap",
151 &PyDict_Type, &dmap,
152 &PyDict_Type, &dmap,
152 &PyInt_Type, &spec_obj,
153 &PyInt_Type, &spec_obj,
153 &PyFunction_Type, &normcase_fallback))
154 &PyFunction_Type, &normcase_fallback))
154 goto quit;
155 goto quit;
155
156
156 spec = (int)PyInt_AS_LONG(spec_obj);
157 spec = (int)PyInt_AS_LONG(spec_obj);
157 switch (spec) {
158 switch (spec) {
158 case NORMCASE_LOWER:
159 case NORMCASE_LOWER:
159 table = lowertable;
160 table = lowertable;
160 break;
161 break;
161 case NORMCASE_UPPER:
162 case NORMCASE_UPPER:
162 table = uppertable;
163 table = uppertable;
163 break;
164 break;
164 case NORMCASE_OTHER:
165 case NORMCASE_OTHER:
165 table = NULL;
166 table = NULL;
166 break;
167 break;
167 default:
168 default:
168 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
169 PyErr_SetString(PyExc_TypeError, "invalid normcasespec");
169 goto quit;
170 goto quit;
170 }
171 }
171
172
172 /* Add some more entries to deal with additions outside this
173 /* Add some more entries to deal with additions outside this
173 function. */
174 function. */
174 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
175 file_foldmap = _dict_new_presized((PyDict_Size(dmap) / 10) * 11);
175 if (file_foldmap == NULL)
176 if (file_foldmap == NULL)
176 goto quit;
177 goto quit;
177
178
178 while (PyDict_Next(dmap, &pos, &k, &v)) {
179 while (PyDict_Next(dmap, &pos, &k, &v)) {
179 if (!dirstate_tuple_check(v)) {
180 if (!dirstate_tuple_check(v)) {
180 PyErr_SetString(PyExc_TypeError,
181 PyErr_SetString(PyExc_TypeError,
181 "expected a dirstate tuple");
182 "expected a dirstate tuple");
182 goto quit;
183 goto quit;
183 }
184 }
184
185
185 tuple = (dirstateTupleObject *)v;
186 tuple = (dirstateTupleObject *)v;
186 if (tuple->state != 'r') {
187 if (tuple->state != 'r') {
187 PyObject *normed;
188 PyObject *normed;
188 if (table != NULL) {
189 if (table != NULL) {
189 normed = _asciitransform(k, table,
190 normed = _asciitransform(k, table,
190 normcase_fallback);
191 normcase_fallback);
191 } else {
192 } else {
192 normed = PyObject_CallFunctionObjArgs(
193 normed = PyObject_CallFunctionObjArgs(
193 normcase_fallback, k, NULL);
194 normcase_fallback, k, NULL);
194 }
195 }
195
196
196 if (normed == NULL)
197 if (normed == NULL)
197 goto quit;
198 goto quit;
198 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
199 if (PyDict_SetItem(file_foldmap, normed, k) == -1) {
199 Py_DECREF(normed);
200 Py_DECREF(normed);
200 goto quit;
201 goto quit;
201 }
202 }
202 Py_DECREF(normed);
203 Py_DECREF(normed);
203 }
204 }
204 }
205 }
205 return file_foldmap;
206 return file_foldmap;
206 quit:
207 quit:
207 Py_XDECREF(file_foldmap);
208 Py_XDECREF(file_foldmap);
208 return NULL;
209 return NULL;
209 }
210 }
@@ -1,88 +1,57 b''
1 /*
1 /*
2 util.h - utility functions for interfacing with the various python APIs.
2 charencode.h - miscellaneous character encoding
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_CHARENCODE_H_
9 #define _HG_UTIL_H_
9 #define _HG_CHARENCODE_H_
10
11 #include "compat.h"
12
13 #if PY_MAJOR_VERSION >= 3
14 #define IS_PY3K
15 #endif
16
10
17 typedef struct {
11 #include <Python.h>
18 PyObject_HEAD
12 #include "compat.h"
19 char state;
20 int mode;
21 int size;
22 int mtime;
23 } dirstateTupleObject;
24
25 extern PyTypeObject dirstateTupleType;
26 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateTupleType)
27
13
28 /* This should be kept in sync with normcasespecs in encoding.py. */
14 /* This should be kept in sync with normcasespecs in encoding.py. */
29 enum normcase_spec {
15 enum normcase_spec {
30 NORMCASE_LOWER = -1,
16 NORMCASE_LOWER = -1,
31 NORMCASE_UPPER = 1,
17 NORMCASE_UPPER = 1,
32 NORMCASE_OTHER = 0
18 NORMCASE_OTHER = 0
33 };
19 };
34
20
35 #define MIN(a, b) (((a)<(b))?(a):(b))
21 PyObject *unhexlify(const char *str, int len);
36 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
22 PyObject *asciilower(PyObject *self, PyObject *args);
37 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
23 PyObject *asciiupper(PyObject *self, PyObject *args);
38 #define true 1
24 PyObject *make_file_foldmap(PyObject *self, PyObject *args);
39 #define false 0
40 typedef unsigned char bool;
41 #else
42 #include <stdbool.h>
43 #endif
44
45 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
46 {
47 /* _PyDict_NewPresized expects a minused parameter, but it actually
48 creates a dictionary that's the nearest power of two bigger than the
49 parameter. For example, with the initial minused = 1000, the
50 dictionary created has size 1024. Of course in a lot of cases that
51 can be greater than the maximum load factor Python's dict object
52 expects (= 2/3), so as soon as we cross the threshold we'll resize
53 anyway. So create a dictionary that's at least 3/2 the size. */
54 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
55 }
56
25
57 static const int8_t hextable[256] = {
26 static const int8_t hextable[256] = {
58 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
27 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
59 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
28 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
60 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
29 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
61 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
30 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
62 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
31 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
63 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
64 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
33 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
65 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
66 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
67 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
68 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
38 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
70 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
39 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
71 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
40 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
72 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
41 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
73 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
42 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
74 };
43 };
75
44
76 static inline int hexdigit(const char *p, Py_ssize_t off)
45 static inline int hexdigit(const char *p, Py_ssize_t off)
77 {
46 {
78 int8_t val = hextable[(unsigned char)p[off]];
47 int8_t val = hextable[(unsigned char)p[off]];
79
48
80 if (val >= 0) {
49 if (val >= 0) {
81 return val;
50 return val;
82 }
51 }
83
52
84 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
53 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
85 return 0;
54 return 0;
86 }
55 }
87
56
88 #endif /* _HG_UTIL_H_ */
57 #endif /* _HG_CHARENCODE_H_ */
@@ -1,939 +1,937 b''
1 /*
1 /*
2 * manifest.c - manifest type that does on-demand parsing.
2 * manifest.c - manifest type that does on-demand parsing.
3 *
3 *
4 * Copyright 2015, Google Inc.
4 * Copyright 2015, Google Inc.
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 #include <Python.h>
9 #include <Python.h>
10
10
11 #include <assert.h>
11 #include <assert.h>
12 #include <string.h>
12 #include <string.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14
14
15 #include "charencode.h"
15 #include "util.h"
16 #include "util.h"
16
17
17 #define DEFAULT_LINES 100000
18 #define DEFAULT_LINES 100000
18
19
19 typedef struct {
20 typedef struct {
20 char *start;
21 char *start;
21 Py_ssize_t len; /* length of line including terminal newline */
22 Py_ssize_t len; /* length of line including terminal newline */
22 char hash_suffix;
23 char hash_suffix;
23 bool from_malloc;
24 bool from_malloc;
24 bool deleted;
25 bool deleted;
25 } line;
26 } line;
26
27
27 typedef struct {
28 typedef struct {
28 PyObject_HEAD
29 PyObject_HEAD
29 PyObject *pydata;
30 PyObject *pydata;
30 line *lines;
31 line *lines;
31 int numlines; /* number of line entries */
32 int numlines; /* number of line entries */
32 int livelines; /* number of non-deleted lines */
33 int livelines; /* number of non-deleted lines */
33 int maxlines; /* allocated number of lines */
34 int maxlines; /* allocated number of lines */
34 bool dirty;
35 bool dirty;
35 } lazymanifest;
36 } lazymanifest;
36
37
37 #define MANIFEST_OOM -1
38 #define MANIFEST_OOM -1
38 #define MANIFEST_NOT_SORTED -2
39 #define MANIFEST_NOT_SORTED -2
39 #define MANIFEST_MALFORMED -3
40 #define MANIFEST_MALFORMED -3
40
41
41 /* defined in charencode.c */
42 PyObject *unhexlify(const char *str, int len);
43
44 /* get the length of the path for a line */
42 /* get the length of the path for a line */
45 static size_t pathlen(line *l) {
43 static size_t pathlen(line *l) {
46 return strlen(l->start);
44 return strlen(l->start);
47 }
45 }
48
46
49 /* get the node value of a single line */
47 /* get the node value of a single line */
50 static PyObject *nodeof(line *l) {
48 static PyObject *nodeof(line *l) {
51 char *s = l->start;
49 char *s = l->start;
52 ssize_t llen = pathlen(l);
50 ssize_t llen = pathlen(l);
53 PyObject *hash = unhexlify(s + llen + 1, 40);
51 PyObject *hash = unhexlify(s + llen + 1, 40);
54 if (!hash) {
52 if (!hash) {
55 return NULL;
53 return NULL;
56 }
54 }
57 if (l->hash_suffix != '\0') {
55 if (l->hash_suffix != '\0') {
58 char newhash[21];
56 char newhash[21];
59 memcpy(newhash, PyBytes_AsString(hash), 20);
57 memcpy(newhash, PyBytes_AsString(hash), 20);
60 Py_DECREF(hash);
58 Py_DECREF(hash);
61 newhash[20] = l->hash_suffix;
59 newhash[20] = l->hash_suffix;
62 hash = PyBytes_FromStringAndSize(newhash, 21);
60 hash = PyBytes_FromStringAndSize(newhash, 21);
63 }
61 }
64 return hash;
62 return hash;
65 }
63 }
66
64
67 /* get the node hash and flags of a line as a tuple */
65 /* get the node hash and flags of a line as a tuple */
68 static PyObject *hashflags(line *l)
66 static PyObject *hashflags(line *l)
69 {
67 {
70 char *s = l->start;
68 char *s = l->start;
71 size_t plen = pathlen(l);
69 size_t plen = pathlen(l);
72 PyObject *hash = nodeof(l);
70 PyObject *hash = nodeof(l);
73
71
74 /* 40 for hash, 1 for null byte, 1 for newline */
72 /* 40 for hash, 1 for null byte, 1 for newline */
75 size_t hplen = plen + 42;
73 size_t hplen = plen + 42;
76 Py_ssize_t flen = l->len - hplen;
74 Py_ssize_t flen = l->len - hplen;
77 PyObject *flags;
75 PyObject *flags;
78 PyObject *tup;
76 PyObject *tup;
79
77
80 if (!hash)
78 if (!hash)
81 return NULL;
79 return NULL;
82 flags = PyBytes_FromStringAndSize(s + hplen - 1, flen);
80 flags = PyBytes_FromStringAndSize(s + hplen - 1, flen);
83 if (!flags) {
81 if (!flags) {
84 Py_DECREF(hash);
82 Py_DECREF(hash);
85 return NULL;
83 return NULL;
86 }
84 }
87 tup = PyTuple_Pack(2, hash, flags);
85 tup = PyTuple_Pack(2, hash, flags);
88 Py_DECREF(flags);
86 Py_DECREF(flags);
89 Py_DECREF(hash);
87 Py_DECREF(hash);
90 return tup;
88 return tup;
91 }
89 }
92
90
93 /* if we're about to run out of space in the line index, add more */
91 /* if we're about to run out of space in the line index, add more */
94 static bool realloc_if_full(lazymanifest *self)
92 static bool realloc_if_full(lazymanifest *self)
95 {
93 {
96 if (self->numlines == self->maxlines) {
94 if (self->numlines == self->maxlines) {
97 self->maxlines *= 2;
95 self->maxlines *= 2;
98 self->lines = realloc(self->lines, self->maxlines * sizeof(line));
96 self->lines = realloc(self->lines, self->maxlines * sizeof(line));
99 }
97 }
100 return !!self->lines;
98 return !!self->lines;
101 }
99 }
102
100
103 /*
101 /*
104 * Find the line boundaries in the manifest that 'data' points to and store
102 * Find the line boundaries in the manifest that 'data' points to and store
105 * information about each line in 'self'.
103 * information about each line in 'self'.
106 */
104 */
107 static int find_lines(lazymanifest *self, char *data, Py_ssize_t len)
105 static int find_lines(lazymanifest *self, char *data, Py_ssize_t len)
108 {
106 {
109 char *prev = NULL;
107 char *prev = NULL;
110 while (len > 0) {
108 while (len > 0) {
111 line *l;
109 line *l;
112 char *next = memchr(data, '\n', len);
110 char *next = memchr(data, '\n', len);
113 if (!next) {
111 if (!next) {
114 return MANIFEST_MALFORMED;
112 return MANIFEST_MALFORMED;
115 }
113 }
116 next++; /* advance past newline */
114 next++; /* advance past newline */
117 if (!realloc_if_full(self)) {
115 if (!realloc_if_full(self)) {
118 return MANIFEST_OOM; /* no memory */
116 return MANIFEST_OOM; /* no memory */
119 }
117 }
120 if (prev && strcmp(prev, data) > -1) {
118 if (prev && strcmp(prev, data) > -1) {
121 /* This data isn't sorted, so we have to abort. */
119 /* This data isn't sorted, so we have to abort. */
122 return MANIFEST_NOT_SORTED;
120 return MANIFEST_NOT_SORTED;
123 }
121 }
124 l = self->lines + ((self->numlines)++);
122 l = self->lines + ((self->numlines)++);
125 l->start = data;
123 l->start = data;
126 l->len = next - data;
124 l->len = next - data;
127 l->hash_suffix = '\0';
125 l->hash_suffix = '\0';
128 l->from_malloc = false;
126 l->from_malloc = false;
129 l->deleted = false;
127 l->deleted = false;
130 len = len - l->len;
128 len = len - l->len;
131 prev = data;
129 prev = data;
132 data = next;
130 data = next;
133 }
131 }
134 self->livelines = self->numlines;
132 self->livelines = self->numlines;
135 return 0;
133 return 0;
136 }
134 }
137
135
138 static int lazymanifest_init(lazymanifest *self, PyObject *args)
136 static int lazymanifest_init(lazymanifest *self, PyObject *args)
139 {
137 {
140 char *data;
138 char *data;
141 Py_ssize_t len;
139 Py_ssize_t len;
142 int err, ret;
140 int err, ret;
143 PyObject *pydata;
141 PyObject *pydata;
144 if (!PyArg_ParseTuple(args, "S", &pydata)) {
142 if (!PyArg_ParseTuple(args, "S", &pydata)) {
145 return -1;
143 return -1;
146 }
144 }
147 err = PyBytes_AsStringAndSize(pydata, &data, &len);
145 err = PyBytes_AsStringAndSize(pydata, &data, &len);
148
146
149 self->dirty = false;
147 self->dirty = false;
150 if (err == -1)
148 if (err == -1)
151 return -1;
149 return -1;
152 self->pydata = pydata;
150 self->pydata = pydata;
153 Py_INCREF(self->pydata);
151 Py_INCREF(self->pydata);
154 Py_BEGIN_ALLOW_THREADS
152 Py_BEGIN_ALLOW_THREADS
155 self->lines = malloc(DEFAULT_LINES * sizeof(line));
153 self->lines = malloc(DEFAULT_LINES * sizeof(line));
156 self->maxlines = DEFAULT_LINES;
154 self->maxlines = DEFAULT_LINES;
157 self->numlines = 0;
155 self->numlines = 0;
158 if (!self->lines)
156 if (!self->lines)
159 ret = MANIFEST_OOM;
157 ret = MANIFEST_OOM;
160 else
158 else
161 ret = find_lines(self, data, len);
159 ret = find_lines(self, data, len);
162 Py_END_ALLOW_THREADS
160 Py_END_ALLOW_THREADS
163 switch (ret) {
161 switch (ret) {
164 case 0:
162 case 0:
165 break;
163 break;
166 case MANIFEST_OOM:
164 case MANIFEST_OOM:
167 PyErr_NoMemory();
165 PyErr_NoMemory();
168 break;
166 break;
169 case MANIFEST_NOT_SORTED:
167 case MANIFEST_NOT_SORTED:
170 PyErr_Format(PyExc_ValueError,
168 PyErr_Format(PyExc_ValueError,
171 "Manifest lines not in sorted order.");
169 "Manifest lines not in sorted order.");
172 break;
170 break;
173 case MANIFEST_MALFORMED:
171 case MANIFEST_MALFORMED:
174 PyErr_Format(PyExc_ValueError,
172 PyErr_Format(PyExc_ValueError,
175 "Manifest did not end in a newline.");
173 "Manifest did not end in a newline.");
176 break;
174 break;
177 default:
175 default:
178 PyErr_Format(PyExc_ValueError,
176 PyErr_Format(PyExc_ValueError,
179 "Unknown problem parsing manifest.");
177 "Unknown problem parsing manifest.");
180 }
178 }
181 return ret == 0 ? 0 : -1;
179 return ret == 0 ? 0 : -1;
182 }
180 }
183
181
184 static void lazymanifest_dealloc(lazymanifest *self)
182 static void lazymanifest_dealloc(lazymanifest *self)
185 {
183 {
186 /* free any extra lines we had to allocate */
184 /* free any extra lines we had to allocate */
187 int i;
185 int i;
188 for (i = 0; i < self->numlines; i++) {
186 for (i = 0; i < self->numlines; i++) {
189 if (self->lines[i].from_malloc) {
187 if (self->lines[i].from_malloc) {
190 free(self->lines[i].start);
188 free(self->lines[i].start);
191 }
189 }
192 }
190 }
193 if (self->lines) {
191 if (self->lines) {
194 free(self->lines);
192 free(self->lines);
195 self->lines = NULL;
193 self->lines = NULL;
196 }
194 }
197 if (self->pydata) {
195 if (self->pydata) {
198 Py_DECREF(self->pydata);
196 Py_DECREF(self->pydata);
199 self->pydata = NULL;
197 self->pydata = NULL;
200 }
198 }
201 PyObject_Del(self);
199 PyObject_Del(self);
202 }
200 }
203
201
204 /* iteration support */
202 /* iteration support */
205
203
206 typedef struct {
204 typedef struct {
207 PyObject_HEAD lazymanifest *m;
205 PyObject_HEAD lazymanifest *m;
208 Py_ssize_t pos;
206 Py_ssize_t pos;
209 } lmIter;
207 } lmIter;
210
208
211 static void lmiter_dealloc(PyObject *o)
209 static void lmiter_dealloc(PyObject *o)
212 {
210 {
213 lmIter *self = (lmIter *)o;
211 lmIter *self = (lmIter *)o;
214 Py_DECREF(self->m);
212 Py_DECREF(self->m);
215 PyObject_Del(self);
213 PyObject_Del(self);
216 }
214 }
217
215
218 static line *lmiter_nextline(lmIter *self)
216 static line *lmiter_nextline(lmIter *self)
219 {
217 {
220 do {
218 do {
221 self->pos++;
219 self->pos++;
222 if (self->pos >= self->m->numlines) {
220 if (self->pos >= self->m->numlines) {
223 return NULL;
221 return NULL;
224 }
222 }
225 /* skip over deleted manifest entries */
223 /* skip over deleted manifest entries */
226 } while (self->m->lines[self->pos].deleted);
224 } while (self->m->lines[self->pos].deleted);
227 return self->m->lines + self->pos;
225 return self->m->lines + self->pos;
228 }
226 }
229
227
230 static PyObject *lmiter_iterentriesnext(PyObject *o)
228 static PyObject *lmiter_iterentriesnext(PyObject *o)
231 {
229 {
232 size_t pl;
230 size_t pl;
233 line *l;
231 line *l;
234 Py_ssize_t consumed;
232 Py_ssize_t consumed;
235 PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
233 PyObject *ret = NULL, *path = NULL, *hash = NULL, *flags = NULL;
236 l = lmiter_nextline((lmIter *)o);
234 l = lmiter_nextline((lmIter *)o);
237 if (!l) {
235 if (!l) {
238 goto done;
236 goto done;
239 }
237 }
240 pl = pathlen(l);
238 pl = pathlen(l);
241 path = PyBytes_FromStringAndSize(l->start, pl);
239 path = PyBytes_FromStringAndSize(l->start, pl);
242 hash = nodeof(l);
240 hash = nodeof(l);
243 consumed = pl + 41;
241 consumed = pl + 41;
244 flags = PyBytes_FromStringAndSize(l->start + consumed,
242 flags = PyBytes_FromStringAndSize(l->start + consumed,
245 l->len - consumed - 1);
243 l->len - consumed - 1);
246 if (!path || !hash || !flags) {
244 if (!path || !hash || !flags) {
247 goto done;
245 goto done;
248 }
246 }
249 ret = PyTuple_Pack(3, path, hash, flags);
247 ret = PyTuple_Pack(3, path, hash, flags);
250 done:
248 done:
251 Py_XDECREF(path);
249 Py_XDECREF(path);
252 Py_XDECREF(hash);
250 Py_XDECREF(hash);
253 Py_XDECREF(flags);
251 Py_XDECREF(flags);
254 return ret;
252 return ret;
255 }
253 }
256
254
257 #ifdef IS_PY3K
255 #ifdef IS_PY3K
258 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
256 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
259 #else
257 #else
260 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
258 #define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
261 | Py_TPFLAGS_HAVE_ITER
259 | Py_TPFLAGS_HAVE_ITER
262 #endif
260 #endif
263
261
264 static PyTypeObject lazymanifestEntriesIterator = {
262 static PyTypeObject lazymanifestEntriesIterator = {
265 PyVarObject_HEAD_INIT(NULL, 0)
263 PyVarObject_HEAD_INIT(NULL, 0)
266 "parsers.lazymanifest.entriesiterator", /*tp_name */
264 "parsers.lazymanifest.entriesiterator", /*tp_name */
267 sizeof(lmIter), /*tp_basicsize */
265 sizeof(lmIter), /*tp_basicsize */
268 0, /*tp_itemsize */
266 0, /*tp_itemsize */
269 lmiter_dealloc, /*tp_dealloc */
267 lmiter_dealloc, /*tp_dealloc */
270 0, /*tp_print */
268 0, /*tp_print */
271 0, /*tp_getattr */
269 0, /*tp_getattr */
272 0, /*tp_setattr */
270 0, /*tp_setattr */
273 0, /*tp_compare */
271 0, /*tp_compare */
274 0, /*tp_repr */
272 0, /*tp_repr */
275 0, /*tp_as_number */
273 0, /*tp_as_number */
276 0, /*tp_as_sequence */
274 0, /*tp_as_sequence */
277 0, /*tp_as_mapping */
275 0, /*tp_as_mapping */
278 0, /*tp_hash */
276 0, /*tp_hash */
279 0, /*tp_call */
277 0, /*tp_call */
280 0, /*tp_str */
278 0, /*tp_str */
281 0, /*tp_getattro */
279 0, /*tp_getattro */
282 0, /*tp_setattro */
280 0, /*tp_setattro */
283 0, /*tp_as_buffer */
281 0, /*tp_as_buffer */
284 LAZYMANIFESTENTRIESITERATOR_TPFLAGS, /* tp_flags */
282 LAZYMANIFESTENTRIESITERATOR_TPFLAGS, /* tp_flags */
285 "Iterator for 3-tuples in a lazymanifest.", /* tp_doc */
283 "Iterator for 3-tuples in a lazymanifest.", /* tp_doc */
286 0, /* tp_traverse */
284 0, /* tp_traverse */
287 0, /* tp_clear */
285 0, /* tp_clear */
288 0, /* tp_richcompare */
286 0, /* tp_richcompare */
289 0, /* tp_weaklistoffset */
287 0, /* tp_weaklistoffset */
290 PyObject_SelfIter, /* tp_iter: __iter__() method */
288 PyObject_SelfIter, /* tp_iter: __iter__() method */
291 lmiter_iterentriesnext, /* tp_iternext: next() method */
289 lmiter_iterentriesnext, /* tp_iternext: next() method */
292 };
290 };
293
291
294 static PyObject *lmiter_iterkeysnext(PyObject *o)
292 static PyObject *lmiter_iterkeysnext(PyObject *o)
295 {
293 {
296 size_t pl;
294 size_t pl;
297 line *l = lmiter_nextline((lmIter *)o);
295 line *l = lmiter_nextline((lmIter *)o);
298 if (!l) {
296 if (!l) {
299 return NULL;
297 return NULL;
300 }
298 }
301 pl = pathlen(l);
299 pl = pathlen(l);
302 return PyBytes_FromStringAndSize(l->start, pl);
300 return PyBytes_FromStringAndSize(l->start, pl);
303 }
301 }
304
302
305 #ifdef IS_PY3K
303 #ifdef IS_PY3K
306 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
304 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT
307 #else
305 #else
308 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
306 #define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \
309 | Py_TPFLAGS_HAVE_ITER
307 | Py_TPFLAGS_HAVE_ITER
310 #endif
308 #endif
311
309
312 static PyTypeObject lazymanifestKeysIterator = {
310 static PyTypeObject lazymanifestKeysIterator = {
313 PyVarObject_HEAD_INIT(NULL, 0)
311 PyVarObject_HEAD_INIT(NULL, 0)
314 "parsers.lazymanifest.keysiterator", /*tp_name */
312 "parsers.lazymanifest.keysiterator", /*tp_name */
315 sizeof(lmIter), /*tp_basicsize */
313 sizeof(lmIter), /*tp_basicsize */
316 0, /*tp_itemsize */
314 0, /*tp_itemsize */
317 lmiter_dealloc, /*tp_dealloc */
315 lmiter_dealloc, /*tp_dealloc */
318 0, /*tp_print */
316 0, /*tp_print */
319 0, /*tp_getattr */
317 0, /*tp_getattr */
320 0, /*tp_setattr */
318 0, /*tp_setattr */
321 0, /*tp_compare */
319 0, /*tp_compare */
322 0, /*tp_repr */
320 0, /*tp_repr */
323 0, /*tp_as_number */
321 0, /*tp_as_number */
324 0, /*tp_as_sequence */
322 0, /*tp_as_sequence */
325 0, /*tp_as_mapping */
323 0, /*tp_as_mapping */
326 0, /*tp_hash */
324 0, /*tp_hash */
327 0, /*tp_call */
325 0, /*tp_call */
328 0, /*tp_str */
326 0, /*tp_str */
329 0, /*tp_getattro */
327 0, /*tp_getattro */
330 0, /*tp_setattro */
328 0, /*tp_setattro */
331 0, /*tp_as_buffer */
329 0, /*tp_as_buffer */
332 LAZYMANIFESTKEYSITERATOR_TPFLAGS, /* tp_flags */
330 LAZYMANIFESTKEYSITERATOR_TPFLAGS, /* tp_flags */
333 "Keys iterator for a lazymanifest.", /* tp_doc */
331 "Keys iterator for a lazymanifest.", /* tp_doc */
334 0, /* tp_traverse */
332 0, /* tp_traverse */
335 0, /* tp_clear */
333 0, /* tp_clear */
336 0, /* tp_richcompare */
334 0, /* tp_richcompare */
337 0, /* tp_weaklistoffset */
335 0, /* tp_weaklistoffset */
338 PyObject_SelfIter, /* tp_iter: __iter__() method */
336 PyObject_SelfIter, /* tp_iter: __iter__() method */
339 lmiter_iterkeysnext, /* tp_iternext: next() method */
337 lmiter_iterkeysnext, /* tp_iternext: next() method */
340 };
338 };
341
339
342 static lazymanifest *lazymanifest_copy(lazymanifest *self);
340 static lazymanifest *lazymanifest_copy(lazymanifest *self);
343
341
344 static PyObject *lazymanifest_getentriesiter(lazymanifest *self)
342 static PyObject *lazymanifest_getentriesiter(lazymanifest *self)
345 {
343 {
346 lmIter *i = NULL;
344 lmIter *i = NULL;
347 lazymanifest *t = lazymanifest_copy(self);
345 lazymanifest *t = lazymanifest_copy(self);
348 if (!t) {
346 if (!t) {
349 PyErr_NoMemory();
347 PyErr_NoMemory();
350 return NULL;
348 return NULL;
351 }
349 }
352 i = PyObject_New(lmIter, &lazymanifestEntriesIterator);
350 i = PyObject_New(lmIter, &lazymanifestEntriesIterator);
353 if (i) {
351 if (i) {
354 i->m = t;
352 i->m = t;
355 i->pos = -1;
353 i->pos = -1;
356 } else {
354 } else {
357 Py_DECREF(t);
355 Py_DECREF(t);
358 PyErr_NoMemory();
356 PyErr_NoMemory();
359 }
357 }
360 return (PyObject *)i;
358 return (PyObject *)i;
361 }
359 }
362
360
363 static PyObject *lazymanifest_getkeysiter(lazymanifest *self)
361 static PyObject *lazymanifest_getkeysiter(lazymanifest *self)
364 {
362 {
365 lmIter *i = NULL;
363 lmIter *i = NULL;
366 lazymanifest *t = lazymanifest_copy(self);
364 lazymanifest *t = lazymanifest_copy(self);
367 if (!t) {
365 if (!t) {
368 PyErr_NoMemory();
366 PyErr_NoMemory();
369 return NULL;
367 return NULL;
370 }
368 }
371 i = PyObject_New(lmIter, &lazymanifestKeysIterator);
369 i = PyObject_New(lmIter, &lazymanifestKeysIterator);
372 if (i) {
370 if (i) {
373 i->m = t;
371 i->m = t;
374 i->pos = -1;
372 i->pos = -1;
375 } else {
373 } else {
376 Py_DECREF(t);
374 Py_DECREF(t);
377 PyErr_NoMemory();
375 PyErr_NoMemory();
378 }
376 }
379 return (PyObject *)i;
377 return (PyObject *)i;
380 }
378 }
381
379
382 /* __getitem__ and __setitem__ support */
380 /* __getitem__ and __setitem__ support */
383
381
384 static Py_ssize_t lazymanifest_size(lazymanifest *self)
382 static Py_ssize_t lazymanifest_size(lazymanifest *self)
385 {
383 {
386 return self->livelines;
384 return self->livelines;
387 }
385 }
388
386
389 static int linecmp(const void *left, const void *right)
387 static int linecmp(const void *left, const void *right)
390 {
388 {
391 return strcmp(((const line *)left)->start,
389 return strcmp(((const line *)left)->start,
392 ((const line *)right)->start);
390 ((const line *)right)->start);
393 }
391 }
394
392
395 static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
393 static PyObject *lazymanifest_getitem(lazymanifest *self, PyObject *key)
396 {
394 {
397 line needle;
395 line needle;
398 line *hit;
396 line *hit;
399 if (!PyBytes_Check(key)) {
397 if (!PyBytes_Check(key)) {
400 PyErr_Format(PyExc_TypeError,
398 PyErr_Format(PyExc_TypeError,
401 "getitem: manifest keys must be a string.");
399 "getitem: manifest keys must be a string.");
402 return NULL;
400 return NULL;
403 }
401 }
404 needle.start = PyBytes_AsString(key);
402 needle.start = PyBytes_AsString(key);
405 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
403 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
406 &linecmp);
404 &linecmp);
407 if (!hit || hit->deleted) {
405 if (!hit || hit->deleted) {
408 PyErr_Format(PyExc_KeyError, "No such manifest entry.");
406 PyErr_Format(PyExc_KeyError, "No such manifest entry.");
409 return NULL;
407 return NULL;
410 }
408 }
411 return hashflags(hit);
409 return hashflags(hit);
412 }
410 }
413
411
414 static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
412 static int lazymanifest_delitem(lazymanifest *self, PyObject *key)
415 {
413 {
416 line needle;
414 line needle;
417 line *hit;
415 line *hit;
418 if (!PyBytes_Check(key)) {
416 if (!PyBytes_Check(key)) {
419 PyErr_Format(PyExc_TypeError,
417 PyErr_Format(PyExc_TypeError,
420 "delitem: manifest keys must be a string.");
418 "delitem: manifest keys must be a string.");
421 return -1;
419 return -1;
422 }
420 }
423 needle.start = PyBytes_AsString(key);
421 needle.start = PyBytes_AsString(key);
424 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
422 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
425 &linecmp);
423 &linecmp);
426 if (!hit || hit->deleted) {
424 if (!hit || hit->deleted) {
427 PyErr_Format(PyExc_KeyError,
425 PyErr_Format(PyExc_KeyError,
428 "Tried to delete nonexistent manifest entry.");
426 "Tried to delete nonexistent manifest entry.");
429 return -1;
427 return -1;
430 }
428 }
431 self->dirty = true;
429 self->dirty = true;
432 hit->deleted = true;
430 hit->deleted = true;
433 self->livelines--;
431 self->livelines--;
434 return 0;
432 return 0;
435 }
433 }
436
434
437 /* Do a binary search for the insertion point for new, creating the
435 /* Do a binary search for the insertion point for new, creating the
438 * new entry if needed. */
436 * new entry if needed. */
439 static int internalsetitem(lazymanifest *self, line *new) {
437 static int internalsetitem(lazymanifest *self, line *new) {
440 int start = 0, end = self->numlines;
438 int start = 0, end = self->numlines;
441 while (start < end) {
439 while (start < end) {
442 int pos = start + (end - start) / 2;
440 int pos = start + (end - start) / 2;
443 int c = linecmp(new, self->lines + pos);
441 int c = linecmp(new, self->lines + pos);
444 if (c < 0)
442 if (c < 0)
445 end = pos;
443 end = pos;
446 else if (c > 0)
444 else if (c > 0)
447 start = pos + 1;
445 start = pos + 1;
448 else {
446 else {
449 if (self->lines[pos].deleted)
447 if (self->lines[pos].deleted)
450 self->livelines++;
448 self->livelines++;
451 if (self->lines[pos].from_malloc)
449 if (self->lines[pos].from_malloc)
452 free(self->lines[pos].start);
450 free(self->lines[pos].start);
453 start = pos;
451 start = pos;
454 goto finish;
452 goto finish;
455 }
453 }
456 }
454 }
457 /* being here means we need to do an insert */
455 /* being here means we need to do an insert */
458 if (!realloc_if_full(self)) {
456 if (!realloc_if_full(self)) {
459 PyErr_NoMemory();
457 PyErr_NoMemory();
460 return -1;
458 return -1;
461 }
459 }
462 memmove(self->lines + start + 1, self->lines + start,
460 memmove(self->lines + start + 1, self->lines + start,
463 (self->numlines - start) * sizeof(line));
461 (self->numlines - start) * sizeof(line));
464 self->numlines++;
462 self->numlines++;
465 self->livelines++;
463 self->livelines++;
466 finish:
464 finish:
467 self->lines[start] = *new;
465 self->lines[start] = *new;
468 self->dirty = true;
466 self->dirty = true;
469 return 0;
467 return 0;
470 }
468 }
471
469
472 static int lazymanifest_setitem(
470 static int lazymanifest_setitem(
473 lazymanifest *self, PyObject *key, PyObject *value)
471 lazymanifest *self, PyObject *key, PyObject *value)
474 {
472 {
475 char *path;
473 char *path;
476 Py_ssize_t plen;
474 Py_ssize_t plen;
477 PyObject *pyhash;
475 PyObject *pyhash;
478 Py_ssize_t hlen;
476 Py_ssize_t hlen;
479 char *hash;
477 char *hash;
480 PyObject *pyflags;
478 PyObject *pyflags;
481 char *flags;
479 char *flags;
482 Py_ssize_t flen;
480 Py_ssize_t flen;
483 size_t dlen;
481 size_t dlen;
484 char *dest;
482 char *dest;
485 int i;
483 int i;
486 line new;
484 line new;
487 if (!PyBytes_Check(key)) {
485 if (!PyBytes_Check(key)) {
488 PyErr_Format(PyExc_TypeError,
486 PyErr_Format(PyExc_TypeError,
489 "setitem: manifest keys must be a string.");
487 "setitem: manifest keys must be a string.");
490 return -1;
488 return -1;
491 }
489 }
492 if (!value) {
490 if (!value) {
493 return lazymanifest_delitem(self, key);
491 return lazymanifest_delitem(self, key);
494 }
492 }
495 if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
493 if (!PyTuple_Check(value) || PyTuple_Size(value) != 2) {
496 PyErr_Format(PyExc_TypeError,
494 PyErr_Format(PyExc_TypeError,
497 "Manifest values must be a tuple of (node, flags).");
495 "Manifest values must be a tuple of (node, flags).");
498 return -1;
496 return -1;
499 }
497 }
500 if (PyBytes_AsStringAndSize(key, &path, &plen) == -1) {
498 if (PyBytes_AsStringAndSize(key, &path, &plen) == -1) {
501 return -1;
499 return -1;
502 }
500 }
503
501
504 pyhash = PyTuple_GetItem(value, 0);
502 pyhash = PyTuple_GetItem(value, 0);
505 if (!PyBytes_Check(pyhash)) {
503 if (!PyBytes_Check(pyhash)) {
506 PyErr_Format(PyExc_TypeError,
504 PyErr_Format(PyExc_TypeError,
507 "node must be a 20-byte string");
505 "node must be a 20-byte string");
508 return -1;
506 return -1;
509 }
507 }
510 hlen = PyBytes_Size(pyhash);
508 hlen = PyBytes_Size(pyhash);
511 /* Some parts of the codebase try and set 21 or 22
509 /* Some parts of the codebase try and set 21 or 22
512 * byte "hash" values in order to perturb things for
510 * byte "hash" values in order to perturb things for
513 * status. We have to preserve at least the 21st
511 * status. We have to preserve at least the 21st
514 * byte. Sigh. If there's a 22nd byte, we drop it on
512 * byte. Sigh. If there's a 22nd byte, we drop it on
515 * the floor, which works fine.
513 * the floor, which works fine.
516 */
514 */
517 if (hlen != 20 && hlen != 21 && hlen != 22) {
515 if (hlen != 20 && hlen != 21 && hlen != 22) {
518 PyErr_Format(PyExc_TypeError,
516 PyErr_Format(PyExc_TypeError,
519 "node must be a 20-byte string");
517 "node must be a 20-byte string");
520 return -1;
518 return -1;
521 }
519 }
522 hash = PyBytes_AsString(pyhash);
520 hash = PyBytes_AsString(pyhash);
523
521
524 pyflags = PyTuple_GetItem(value, 1);
522 pyflags = PyTuple_GetItem(value, 1);
525 if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) {
523 if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) {
526 PyErr_Format(PyExc_TypeError,
524 PyErr_Format(PyExc_TypeError,
527 "flags must a 0 or 1 byte string");
525 "flags must a 0 or 1 byte string");
528 return -1;
526 return -1;
529 }
527 }
530 if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) {
528 if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) {
531 return -1;
529 return -1;
532 }
530 }
533 /* one null byte and one newline */
531 /* one null byte and one newline */
534 dlen = plen + 41 + flen + 1;
532 dlen = plen + 41 + flen + 1;
535 dest = malloc(dlen);
533 dest = malloc(dlen);
536 if (!dest) {
534 if (!dest) {
537 PyErr_NoMemory();
535 PyErr_NoMemory();
538 return -1;
536 return -1;
539 }
537 }
540 memcpy(dest, path, plen + 1);
538 memcpy(dest, path, plen + 1);
541 for (i = 0; i < 20; i++) {
539 for (i = 0; i < 20; i++) {
542 /* Cast to unsigned, so it will not get sign-extended when promoted
540 /* Cast to unsigned, so it will not get sign-extended when promoted
543 * to int (as is done when passing to a variadic function)
541 * to int (as is done when passing to a variadic function)
544 */
542 */
545 sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
543 sprintf(dest + plen + 1 + (i * 2), "%02x", (unsigned char)hash[i]);
546 }
544 }
547 memcpy(dest + plen + 41, flags, flen);
545 memcpy(dest + plen + 41, flags, flen);
548 dest[plen + 41 + flen] = '\n';
546 dest[plen + 41 + flen] = '\n';
549 new.start = dest;
547 new.start = dest;
550 new.len = dlen;
548 new.len = dlen;
551 new.hash_suffix = '\0';
549 new.hash_suffix = '\0';
552 if (hlen > 20) {
550 if (hlen > 20) {
553 new.hash_suffix = hash[20];
551 new.hash_suffix = hash[20];
554 }
552 }
555 new.from_malloc = true; /* is `start` a pointer we allocated? */
553 new.from_malloc = true; /* is `start` a pointer we allocated? */
556 new.deleted = false; /* is this entry deleted? */
554 new.deleted = false; /* is this entry deleted? */
557 if (internalsetitem(self, &new)) {
555 if (internalsetitem(self, &new)) {
558 return -1;
556 return -1;
559 }
557 }
560 return 0;
558 return 0;
561 }
559 }
562
560
563 static PyMappingMethods lazymanifest_mapping_methods = {
561 static PyMappingMethods lazymanifest_mapping_methods = {
564 (lenfunc)lazymanifest_size, /* mp_length */
562 (lenfunc)lazymanifest_size, /* mp_length */
565 (binaryfunc)lazymanifest_getitem, /* mp_subscript */
563 (binaryfunc)lazymanifest_getitem, /* mp_subscript */
566 (objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
564 (objobjargproc)lazymanifest_setitem, /* mp_ass_subscript */
567 };
565 };
568
566
569 /* sequence methods (important or __contains__ builds an iterator) */
567 /* sequence methods (important or __contains__ builds an iterator) */
570
568
571 static int lazymanifest_contains(lazymanifest *self, PyObject *key)
569 static int lazymanifest_contains(lazymanifest *self, PyObject *key)
572 {
570 {
573 line needle;
571 line needle;
574 line *hit;
572 line *hit;
575 if (!PyBytes_Check(key)) {
573 if (!PyBytes_Check(key)) {
576 /* Our keys are always strings, so if the contains
574 /* Our keys are always strings, so if the contains
577 * check is for a non-string, just return false. */
575 * check is for a non-string, just return false. */
578 return 0;
576 return 0;
579 }
577 }
580 needle.start = PyBytes_AsString(key);
578 needle.start = PyBytes_AsString(key);
581 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
579 hit = bsearch(&needle, self->lines, self->numlines, sizeof(line),
582 &linecmp);
580 &linecmp);
583 if (!hit || hit->deleted) {
581 if (!hit || hit->deleted) {
584 return 0;
582 return 0;
585 }
583 }
586 return 1;
584 return 1;
587 }
585 }
588
586
589 static PySequenceMethods lazymanifest_seq_meths = {
587 static PySequenceMethods lazymanifest_seq_meths = {
590 (lenfunc)lazymanifest_size, /* sq_length */
588 (lenfunc)lazymanifest_size, /* sq_length */
591 0, /* sq_concat */
589 0, /* sq_concat */
592 0, /* sq_repeat */
590 0, /* sq_repeat */
593 0, /* sq_item */
591 0, /* sq_item */
594 0, /* sq_slice */
592 0, /* sq_slice */
595 0, /* sq_ass_item */
593 0, /* sq_ass_item */
596 0, /* sq_ass_slice */
594 0, /* sq_ass_slice */
597 (objobjproc)lazymanifest_contains, /* sq_contains */
595 (objobjproc)lazymanifest_contains, /* sq_contains */
598 0, /* sq_inplace_concat */
596 0, /* sq_inplace_concat */
599 0, /* sq_inplace_repeat */
597 0, /* sq_inplace_repeat */
600 };
598 };
601
599
602
600
603 /* Other methods (copy, diff, etc) */
601 /* Other methods (copy, diff, etc) */
604 static PyTypeObject lazymanifestType;
602 static PyTypeObject lazymanifestType;
605
603
606 /* If the manifest has changes, build the new manifest text and reindex it. */
604 /* If the manifest has changes, build the new manifest text and reindex it. */
607 static int compact(lazymanifest *self) {
605 static int compact(lazymanifest *self) {
608 int i;
606 int i;
609 ssize_t need = 0;
607 ssize_t need = 0;
610 char *data;
608 char *data;
611 line *src, *dst;
609 line *src, *dst;
612 PyObject *pydata;
610 PyObject *pydata;
613 if (!self->dirty)
611 if (!self->dirty)
614 return 0;
612 return 0;
615 for (i = 0; i < self->numlines; i++) {
613 for (i = 0; i < self->numlines; i++) {
616 if (!self->lines[i].deleted) {
614 if (!self->lines[i].deleted) {
617 need += self->lines[i].len;
615 need += self->lines[i].len;
618 }
616 }
619 }
617 }
620 pydata = PyBytes_FromStringAndSize(NULL, need);
618 pydata = PyBytes_FromStringAndSize(NULL, need);
621 if (!pydata)
619 if (!pydata)
622 return -1;
620 return -1;
623 data = PyBytes_AsString(pydata);
621 data = PyBytes_AsString(pydata);
624 if (!data) {
622 if (!data) {
625 return -1;
623 return -1;
626 }
624 }
627 src = self->lines;
625 src = self->lines;
628 dst = self->lines;
626 dst = self->lines;
629 for (i = 0; i < self->numlines; i++, src++) {
627 for (i = 0; i < self->numlines; i++, src++) {
630 char *tofree = NULL;
628 char *tofree = NULL;
631 if (src->from_malloc) {
629 if (src->from_malloc) {
632 tofree = src->start;
630 tofree = src->start;
633 }
631 }
634 if (!src->deleted) {
632 if (!src->deleted) {
635 memcpy(data, src->start, src->len);
633 memcpy(data, src->start, src->len);
636 *dst = *src;
634 *dst = *src;
637 dst->start = data;
635 dst->start = data;
638 dst->from_malloc = false;
636 dst->from_malloc = false;
639 data += dst->len;
637 data += dst->len;
640 dst++;
638 dst++;
641 }
639 }
642 free(tofree);
640 free(tofree);
643 }
641 }
644 Py_DECREF(self->pydata);
642 Py_DECREF(self->pydata);
645 self->pydata = pydata;
643 self->pydata = pydata;
646 self->numlines = self->livelines;
644 self->numlines = self->livelines;
647 self->dirty = false;
645 self->dirty = false;
648 return 0;
646 return 0;
649 }
647 }
650
648
651 static PyObject *lazymanifest_text(lazymanifest *self)
649 static PyObject *lazymanifest_text(lazymanifest *self)
652 {
650 {
653 if (compact(self) != 0) {
651 if (compact(self) != 0) {
654 PyErr_NoMemory();
652 PyErr_NoMemory();
655 return NULL;
653 return NULL;
656 }
654 }
657 Py_INCREF(self->pydata);
655 Py_INCREF(self->pydata);
658 return self->pydata;
656 return self->pydata;
659 }
657 }
660
658
661 static lazymanifest *lazymanifest_copy(lazymanifest *self)
659 static lazymanifest *lazymanifest_copy(lazymanifest *self)
662 {
660 {
663 lazymanifest *copy = NULL;
661 lazymanifest *copy = NULL;
664 if (compact(self) != 0) {
662 if (compact(self) != 0) {
665 goto nomem;
663 goto nomem;
666 }
664 }
667 copy = PyObject_New(lazymanifest, &lazymanifestType);
665 copy = PyObject_New(lazymanifest, &lazymanifestType);
668 if (!copy) {
666 if (!copy) {
669 goto nomem;
667 goto nomem;
670 }
668 }
671 copy->numlines = self->numlines;
669 copy->numlines = self->numlines;
672 copy->livelines = self->livelines;
670 copy->livelines = self->livelines;
673 copy->dirty = false;
671 copy->dirty = false;
674 copy->lines = malloc(self->maxlines *sizeof(line));
672 copy->lines = malloc(self->maxlines *sizeof(line));
675 if (!copy->lines) {
673 if (!copy->lines) {
676 goto nomem;
674 goto nomem;
677 }
675 }
678 memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
676 memcpy(copy->lines, self->lines, self->numlines * sizeof(line));
679 copy->maxlines = self->maxlines;
677 copy->maxlines = self->maxlines;
680 copy->pydata = self->pydata;
678 copy->pydata = self->pydata;
681 Py_INCREF(copy->pydata);
679 Py_INCREF(copy->pydata);
682 return copy;
680 return copy;
683 nomem:
681 nomem:
684 PyErr_NoMemory();
682 PyErr_NoMemory();
685 Py_XDECREF(copy);
683 Py_XDECREF(copy);
686 return NULL;
684 return NULL;
687 }
685 }
688
686
689 static lazymanifest *lazymanifest_filtercopy(
687 static lazymanifest *lazymanifest_filtercopy(
690 lazymanifest *self, PyObject *matchfn)
688 lazymanifest *self, PyObject *matchfn)
691 {
689 {
692 lazymanifest *copy = NULL;
690 lazymanifest *copy = NULL;
693 int i;
691 int i;
694 if (!PyCallable_Check(matchfn)) {
692 if (!PyCallable_Check(matchfn)) {
695 PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
693 PyErr_SetString(PyExc_TypeError, "matchfn must be callable");
696 return NULL;
694 return NULL;
697 }
695 }
698 /* compact ourselves first to avoid double-frees later when we
696 /* compact ourselves first to avoid double-frees later when we
699 * compact tmp so that it doesn't have random pointers to our
697 * compact tmp so that it doesn't have random pointers to our
700 * underlying from_malloc-data (self->pydata is safe) */
698 * underlying from_malloc-data (self->pydata is safe) */
701 if (compact(self) != 0) {
699 if (compact(self) != 0) {
702 goto nomem;
700 goto nomem;
703 }
701 }
704 copy = PyObject_New(lazymanifest, &lazymanifestType);
702 copy = PyObject_New(lazymanifest, &lazymanifestType);
705 if (!copy) {
703 if (!copy) {
706 goto nomem;
704 goto nomem;
707 }
705 }
708 copy->dirty = true;
706 copy->dirty = true;
709 copy->lines = malloc(self->maxlines * sizeof(line));
707 copy->lines = malloc(self->maxlines * sizeof(line));
710 if (!copy->lines) {
708 if (!copy->lines) {
711 goto nomem;
709 goto nomem;
712 }
710 }
713 copy->maxlines = self->maxlines;
711 copy->maxlines = self->maxlines;
714 copy->numlines = 0;
712 copy->numlines = 0;
715 copy->pydata = self->pydata;
713 copy->pydata = self->pydata;
716 Py_INCREF(self->pydata);
714 Py_INCREF(self->pydata);
717 for (i = 0; i < self->numlines; i++) {
715 for (i = 0; i < self->numlines; i++) {
718 PyObject *arglist = NULL, *result = NULL;
716 PyObject *arglist = NULL, *result = NULL;
719 arglist = Py_BuildValue("(s)", self->lines[i].start);
717 arglist = Py_BuildValue("(s)", self->lines[i].start);
720 if (!arglist) {
718 if (!arglist) {
721 return NULL;
719 return NULL;
722 }
720 }
723 result = PyObject_CallObject(matchfn, arglist);
721 result = PyObject_CallObject(matchfn, arglist);
724 Py_DECREF(arglist);
722 Py_DECREF(arglist);
725 /* if the callback raised an exception, just let it
723 /* if the callback raised an exception, just let it
726 * through and give up */
724 * through and give up */
727 if (!result) {
725 if (!result) {
728 free(copy->lines);
726 free(copy->lines);
729 Py_DECREF(self->pydata);
727 Py_DECREF(self->pydata);
730 return NULL;
728 return NULL;
731 }
729 }
732 if (PyObject_IsTrue(result)) {
730 if (PyObject_IsTrue(result)) {
733 assert(!(self->lines[i].from_malloc));
731 assert(!(self->lines[i].from_malloc));
734 copy->lines[copy->numlines++] = self->lines[i];
732 copy->lines[copy->numlines++] = self->lines[i];
735 }
733 }
736 Py_DECREF(result);
734 Py_DECREF(result);
737 }
735 }
738 copy->livelines = copy->numlines;
736 copy->livelines = copy->numlines;
739 return copy;
737 return copy;
740 nomem:
738 nomem:
741 PyErr_NoMemory();
739 PyErr_NoMemory();
742 Py_XDECREF(copy);
740 Py_XDECREF(copy);
743 return NULL;
741 return NULL;
744 }
742 }
745
743
746 static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
744 static PyObject *lazymanifest_diff(lazymanifest *self, PyObject *args)
747 {
745 {
748 lazymanifest *other;
746 lazymanifest *other;
749 PyObject *pyclean = NULL;
747 PyObject *pyclean = NULL;
750 bool listclean;
748 bool listclean;
751 PyObject *emptyTup = NULL, *ret = NULL;
749 PyObject *emptyTup = NULL, *ret = NULL;
752 PyObject *es;
750 PyObject *es;
753 int sneedle = 0, oneedle = 0;
751 int sneedle = 0, oneedle = 0;
754 if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
752 if (!PyArg_ParseTuple(args, "O!|O", &lazymanifestType, &other, &pyclean)) {
755 return NULL;
753 return NULL;
756 }
754 }
757 listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
755 listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean);
758 es = PyBytes_FromString("");
756 es = PyBytes_FromString("");
759 if (!es) {
757 if (!es) {
760 goto nomem;
758 goto nomem;
761 }
759 }
762 emptyTup = PyTuple_Pack(2, Py_None, es);
760 emptyTup = PyTuple_Pack(2, Py_None, es);
763 Py_DECREF(es);
761 Py_DECREF(es);
764 if (!emptyTup) {
762 if (!emptyTup) {
765 goto nomem;
763 goto nomem;
766 }
764 }
767 ret = PyDict_New();
765 ret = PyDict_New();
768 if (!ret) {
766 if (!ret) {
769 goto nomem;
767 goto nomem;
770 }
768 }
771 while (sneedle != self->numlines || oneedle != other->numlines) {
769 while (sneedle != self->numlines || oneedle != other->numlines) {
772 line *left = self->lines + sneedle;
770 line *left = self->lines + sneedle;
773 line *right = other->lines + oneedle;
771 line *right = other->lines + oneedle;
774 int result;
772 int result;
775 PyObject *key;
773 PyObject *key;
776 PyObject *outer;
774 PyObject *outer;
777 /* If we're looking at a deleted entry and it's not
775 /* If we're looking at a deleted entry and it's not
778 * the end of the manifest, just skip it. */
776 * the end of the manifest, just skip it. */
779 if (left->deleted && sneedle < self->numlines) {
777 if (left->deleted && sneedle < self->numlines) {
780 sneedle++;
778 sneedle++;
781 continue;
779 continue;
782 }
780 }
783 if (right->deleted && oneedle < other->numlines) {
781 if (right->deleted && oneedle < other->numlines) {
784 oneedle++;
782 oneedle++;
785 continue;
783 continue;
786 }
784 }
787 /* if we're at the end of either manifest, then we
785 /* if we're at the end of either manifest, then we
788 * know the remaining items are adds so we can skip
786 * know the remaining items are adds so we can skip
789 * the strcmp. */
787 * the strcmp. */
790 if (sneedle == self->numlines) {
788 if (sneedle == self->numlines) {
791 result = 1;
789 result = 1;
792 } else if (oneedle == other->numlines) {
790 } else if (oneedle == other->numlines) {
793 result = -1;
791 result = -1;
794 } else {
792 } else {
795 result = linecmp(left, right);
793 result = linecmp(left, right);
796 }
794 }
797 key = result <= 0 ?
795 key = result <= 0 ?
798 PyBytes_FromString(left->start) :
796 PyBytes_FromString(left->start) :
799 PyBytes_FromString(right->start);
797 PyBytes_FromString(right->start);
800 if (!key)
798 if (!key)
801 goto nomem;
799 goto nomem;
802 if (result < 0) {
800 if (result < 0) {
803 PyObject *l = hashflags(left);
801 PyObject *l = hashflags(left);
804 if (!l) {
802 if (!l) {
805 goto nomem;
803 goto nomem;
806 }
804 }
807 outer = PyTuple_Pack(2, l, emptyTup);
805 outer = PyTuple_Pack(2, l, emptyTup);
808 Py_DECREF(l);
806 Py_DECREF(l);
809 if (!outer) {
807 if (!outer) {
810 goto nomem;
808 goto nomem;
811 }
809 }
812 PyDict_SetItem(ret, key, outer);
810 PyDict_SetItem(ret, key, outer);
813 Py_DECREF(outer);
811 Py_DECREF(outer);
814 sneedle++;
812 sneedle++;
815 } else if (result > 0) {
813 } else if (result > 0) {
816 PyObject *r = hashflags(right);
814 PyObject *r = hashflags(right);
817 if (!r) {
815 if (!r) {
818 goto nomem;
816 goto nomem;
819 }
817 }
820 outer = PyTuple_Pack(2, emptyTup, r);
818 outer = PyTuple_Pack(2, emptyTup, r);
821 Py_DECREF(r);
819 Py_DECREF(r);
822 if (!outer) {
820 if (!outer) {
823 goto nomem;
821 goto nomem;
824 }
822 }
825 PyDict_SetItem(ret, key, outer);
823 PyDict_SetItem(ret, key, outer);
826 Py_DECREF(outer);
824 Py_DECREF(outer);
827 oneedle++;
825 oneedle++;
828 } else {
826 } else {
829 /* file exists in both manifests */
827 /* file exists in both manifests */
830 if (left->len != right->len
828 if (left->len != right->len
831 || memcmp(left->start, right->start, left->len)
829 || memcmp(left->start, right->start, left->len)
832 || left->hash_suffix != right->hash_suffix) {
830 || left->hash_suffix != right->hash_suffix) {
833 PyObject *l = hashflags(left);
831 PyObject *l = hashflags(left);
834 PyObject *r;
832 PyObject *r;
835 if (!l) {
833 if (!l) {
836 goto nomem;
834 goto nomem;
837 }
835 }
838 r = hashflags(right);
836 r = hashflags(right);
839 if (!r) {
837 if (!r) {
840 Py_DECREF(l);
838 Py_DECREF(l);
841 goto nomem;
839 goto nomem;
842 }
840 }
843 outer = PyTuple_Pack(2, l, r);
841 outer = PyTuple_Pack(2, l, r);
844 Py_DECREF(l);
842 Py_DECREF(l);
845 Py_DECREF(r);
843 Py_DECREF(r);
846 if (!outer) {
844 if (!outer) {
847 goto nomem;
845 goto nomem;
848 }
846 }
849 PyDict_SetItem(ret, key, outer);
847 PyDict_SetItem(ret, key, outer);
850 Py_DECREF(outer);
848 Py_DECREF(outer);
851 } else if (listclean) {
849 } else if (listclean) {
852 PyDict_SetItem(ret, key, Py_None);
850 PyDict_SetItem(ret, key, Py_None);
853 }
851 }
854 sneedle++;
852 sneedle++;
855 oneedle++;
853 oneedle++;
856 }
854 }
857 Py_DECREF(key);
855 Py_DECREF(key);
858 }
856 }
859 Py_DECREF(emptyTup);
857 Py_DECREF(emptyTup);
860 return ret;
858 return ret;
861 nomem:
859 nomem:
862 PyErr_NoMemory();
860 PyErr_NoMemory();
863 Py_XDECREF(ret);
861 Py_XDECREF(ret);
864 Py_XDECREF(emptyTup);
862 Py_XDECREF(emptyTup);
865 return NULL;
863 return NULL;
866 }
864 }
867
865
868 static PyMethodDef lazymanifest_methods[] = {
866 static PyMethodDef lazymanifest_methods[] = {
869 {"iterkeys", (PyCFunction)lazymanifest_getkeysiter, METH_NOARGS,
867 {"iterkeys", (PyCFunction)lazymanifest_getkeysiter, METH_NOARGS,
870 "Iterate over file names in this lazymanifest."},
868 "Iterate over file names in this lazymanifest."},
871 {"iterentries", (PyCFunction)lazymanifest_getentriesiter, METH_NOARGS,
869 {"iterentries", (PyCFunction)lazymanifest_getentriesiter, METH_NOARGS,
872 "Iterate over (path, nodeid, flags) tuples in this lazymanifest."},
870 "Iterate over (path, nodeid, flags) tuples in this lazymanifest."},
873 {"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
871 {"copy", (PyCFunction)lazymanifest_copy, METH_NOARGS,
874 "Make a copy of this lazymanifest."},
872 "Make a copy of this lazymanifest."},
875 {"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
873 {"filtercopy", (PyCFunction)lazymanifest_filtercopy, METH_O,
876 "Make a copy of this manifest filtered by matchfn."},
874 "Make a copy of this manifest filtered by matchfn."},
877 {"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
875 {"diff", (PyCFunction)lazymanifest_diff, METH_VARARGS,
878 "Compare this lazymanifest to another one."},
876 "Compare this lazymanifest to another one."},
879 {"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
877 {"text", (PyCFunction)lazymanifest_text, METH_NOARGS,
880 "Encode this manifest to text."},
878 "Encode this manifest to text."},
881 {NULL},
879 {NULL},
882 };
880 };
883
881
884 #ifdef IS_PY3K
882 #ifdef IS_PY3K
885 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT
883 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT
886 #else
884 #else
887 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN
885 #define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN
888 #endif
886 #endif
889
887
890 static PyTypeObject lazymanifestType = {
888 static PyTypeObject lazymanifestType = {
891 PyVarObject_HEAD_INIT(NULL, 0)
889 PyVarObject_HEAD_INIT(NULL, 0)
892 "parsers.lazymanifest", /* tp_name */
890 "parsers.lazymanifest", /* tp_name */
893 sizeof(lazymanifest), /* tp_basicsize */
891 sizeof(lazymanifest), /* tp_basicsize */
894 0, /* tp_itemsize */
892 0, /* tp_itemsize */
895 (destructor)lazymanifest_dealloc, /* tp_dealloc */
893 (destructor)lazymanifest_dealloc, /* tp_dealloc */
896 0, /* tp_print */
894 0, /* tp_print */
897 0, /* tp_getattr */
895 0, /* tp_getattr */
898 0, /* tp_setattr */
896 0, /* tp_setattr */
899 0, /* tp_compare */
897 0, /* tp_compare */
900 0, /* tp_repr */
898 0, /* tp_repr */
901 0, /* tp_as_number */
899 0, /* tp_as_number */
902 &lazymanifest_seq_meths, /* tp_as_sequence */
900 &lazymanifest_seq_meths, /* tp_as_sequence */
903 &lazymanifest_mapping_methods, /* tp_as_mapping */
901 &lazymanifest_mapping_methods, /* tp_as_mapping */
904 0, /* tp_hash */
902 0, /* tp_hash */
905 0, /* tp_call */
903 0, /* tp_call */
906 0, /* tp_str */
904 0, /* tp_str */
907 0, /* tp_getattro */
905 0, /* tp_getattro */
908 0, /* tp_setattro */
906 0, /* tp_setattro */
909 0, /* tp_as_buffer */
907 0, /* tp_as_buffer */
910 LAZYMANIFEST_TPFLAGS, /* tp_flags */
908 LAZYMANIFEST_TPFLAGS, /* tp_flags */
911 "TODO(augie)", /* tp_doc */
909 "TODO(augie)", /* tp_doc */
912 0, /* tp_traverse */
910 0, /* tp_traverse */
913 0, /* tp_clear */
911 0, /* tp_clear */
914 0, /* tp_richcompare */
912 0, /* tp_richcompare */
915 0, /* tp_weaklistoffset */
913 0, /* tp_weaklistoffset */
916 (getiterfunc)lazymanifest_getkeysiter, /* tp_iter */
914 (getiterfunc)lazymanifest_getkeysiter, /* tp_iter */
917 0, /* tp_iternext */
915 0, /* tp_iternext */
918 lazymanifest_methods, /* tp_methods */
916 lazymanifest_methods, /* tp_methods */
919 0, /* tp_members */
917 0, /* tp_members */
920 0, /* tp_getset */
918 0, /* tp_getset */
921 0, /* tp_base */
919 0, /* tp_base */
922 0, /* tp_dict */
920 0, /* tp_dict */
923 0, /* tp_descr_get */
921 0, /* tp_descr_get */
924 0, /* tp_descr_set */
922 0, /* tp_descr_set */
925 0, /* tp_dictoffset */
923 0, /* tp_dictoffset */
926 (initproc)lazymanifest_init, /* tp_init */
924 (initproc)lazymanifest_init, /* tp_init */
927 0, /* tp_alloc */
925 0, /* tp_alloc */
928 };
926 };
929
927
930 void manifest_module_init(PyObject * mod)
928 void manifest_module_init(PyObject * mod)
931 {
929 {
932 lazymanifestType.tp_new = PyType_GenericNew;
930 lazymanifestType.tp_new = PyType_GenericNew;
933 if (PyType_Ready(&lazymanifestType) < 0)
931 if (PyType_Ready(&lazymanifestType) < 0)
934 return;
932 return;
935 Py_INCREF(&lazymanifestType);
933 Py_INCREF(&lazymanifestType);
936
934
937 PyModule_AddObject(mod, "lazymanifest",
935 PyModule_AddObject(mod, "lazymanifest",
938 (PyObject *)&lazymanifestType);
936 (PyObject *)&lazymanifestType);
939 }
937 }
@@ -1,806 +1,801 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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 #include <Python.h>
10 #include <Python.h>
11 #include <ctype.h>
11 #include <ctype.h>
12 #include <stddef.h>
12 #include <stddef.h>
13 #include <string.h>
13 #include <string.h>
14
14
15 #include "charencode.h"
15 #include "util.h"
16 #include "util.h"
16 #include "bitmanipulation.h"
17 #include "bitmanipulation.h"
17
18
18 #ifdef IS_PY3K
19 #ifdef IS_PY3K
19 /* The mapping of Python types is meant to be temporary to get Python
20 /* 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
21 * 3 to compile. We should remove this once Python 3 support is fully
21 * supported and proper types are used in the extensions themselves. */
22 * supported and proper types are used in the extensions themselves. */
22 #define PyInt_Type PyLong_Type
23 #define PyInt_Type PyLong_Type
23 #define PyInt_Check PyLong_Check
24 #define PyInt_Check PyLong_Check
24 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AsLong PyLong_AsLong
28 #define PyInt_AsLong PyLong_AsLong
28 #endif
29 #endif
29
30
30 static const char *const versionerrortext = "Python minor version mismatch";
31 static const char *const versionerrortext = "Python minor version mismatch";
31
32
32 /* defined in charencode.c */
33 PyObject *unhexlify(const char *str, int len);
34 PyObject *asciilower(PyObject *self, PyObject *args);
35 PyObject *asciiupper(PyObject *self, PyObject *args);
36 PyObject *make_file_foldmap(PyObject *self, PyObject *args);
37
38 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
33 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
39 {
34 {
40 Py_ssize_t expected_size;
35 Py_ssize_t expected_size;
41
36
42 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
37 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size))
43 return NULL;
38 return NULL;
44
39
45 return _dict_new_presized(expected_size);
40 return _dict_new_presized(expected_size);
46 }
41 }
47
42
48 /*
43 /*
49 * This code assumes that a manifest is stitched together with newline
44 * This code assumes that a manifest is stitched together with newline
50 * ('\n') characters.
45 * ('\n') characters.
51 */
46 */
52 static PyObject *parse_manifest(PyObject *self, PyObject *args)
47 static PyObject *parse_manifest(PyObject *self, PyObject *args)
53 {
48 {
54 PyObject *mfdict, *fdict;
49 PyObject *mfdict, *fdict;
55 char *str, *start, *end;
50 char *str, *start, *end;
56 int len;
51 int len;
57
52
58 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
53 if (!PyArg_ParseTuple(args, "O!O!s#:parse_manifest",
59 &PyDict_Type, &mfdict,
54 &PyDict_Type, &mfdict,
60 &PyDict_Type, &fdict,
55 &PyDict_Type, &fdict,
61 &str, &len))
56 &str, &len))
62 goto quit;
57 goto quit;
63
58
64 start = str;
59 start = str;
65 end = str + len;
60 end = str + len;
66 while (start < end) {
61 while (start < end) {
67 PyObject *file = NULL, *node = NULL;
62 PyObject *file = NULL, *node = NULL;
68 PyObject *flags = NULL;
63 PyObject *flags = NULL;
69 char *zero = NULL, *newline = NULL;
64 char *zero = NULL, *newline = NULL;
70 ptrdiff_t nlen;
65 ptrdiff_t nlen;
71
66
72 zero = memchr(start, '\0', end - start);
67 zero = memchr(start, '\0', end - start);
73 if (!zero) {
68 if (!zero) {
74 PyErr_SetString(PyExc_ValueError,
69 PyErr_SetString(PyExc_ValueError,
75 "manifest entry has no separator");
70 "manifest entry has no separator");
76 goto quit;
71 goto quit;
77 }
72 }
78
73
79 newline = memchr(zero + 1, '\n', end - (zero + 1));
74 newline = memchr(zero + 1, '\n', end - (zero + 1));
80 if (!newline) {
75 if (!newline) {
81 PyErr_SetString(PyExc_ValueError,
76 PyErr_SetString(PyExc_ValueError,
82 "manifest contains trailing garbage");
77 "manifest contains trailing garbage");
83 goto quit;
78 goto quit;
84 }
79 }
85
80
86 file = PyBytes_FromStringAndSize(start, zero - start);
81 file = PyBytes_FromStringAndSize(start, zero - start);
87
82
88 if (!file)
83 if (!file)
89 goto bail;
84 goto bail;
90
85
91 nlen = newline - zero - 1;
86 nlen = newline - zero - 1;
92
87
93 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
88 node = unhexlify(zero + 1, nlen > 40 ? 40 : (int)nlen);
94 if (!node)
89 if (!node)
95 goto bail;
90 goto bail;
96
91
97 if (nlen > 40) {
92 if (nlen > 40) {
98 flags = PyBytes_FromStringAndSize(zero + 41,
93 flags = PyBytes_FromStringAndSize(zero + 41,
99 nlen - 40);
94 nlen - 40);
100 if (!flags)
95 if (!flags)
101 goto bail;
96 goto bail;
102
97
103 if (PyDict_SetItem(fdict, file, flags) == -1)
98 if (PyDict_SetItem(fdict, file, flags) == -1)
104 goto bail;
99 goto bail;
105 }
100 }
106
101
107 if (PyDict_SetItem(mfdict, file, node) == -1)
102 if (PyDict_SetItem(mfdict, file, node) == -1)
108 goto bail;
103 goto bail;
109
104
110 start = newline + 1;
105 start = newline + 1;
111
106
112 Py_XDECREF(flags);
107 Py_XDECREF(flags);
113 Py_XDECREF(node);
108 Py_XDECREF(node);
114 Py_XDECREF(file);
109 Py_XDECREF(file);
115 continue;
110 continue;
116 bail:
111 bail:
117 Py_XDECREF(flags);
112 Py_XDECREF(flags);
118 Py_XDECREF(node);
113 Py_XDECREF(node);
119 Py_XDECREF(file);
114 Py_XDECREF(file);
120 goto quit;
115 goto quit;
121 }
116 }
122
117
123 Py_INCREF(Py_None);
118 Py_INCREF(Py_None);
124 return Py_None;
119 return Py_None;
125 quit:
120 quit:
126 return NULL;
121 return NULL;
127 }
122 }
128
123
129 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
124 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
130 int size, int mtime)
125 int size, int mtime)
131 {
126 {
132 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
127 dirstateTupleObject *t = PyObject_New(dirstateTupleObject,
133 &dirstateTupleType);
128 &dirstateTupleType);
134 if (!t)
129 if (!t)
135 return NULL;
130 return NULL;
136 t->state = state;
131 t->state = state;
137 t->mode = mode;
132 t->mode = mode;
138 t->size = size;
133 t->size = size;
139 t->mtime = mtime;
134 t->mtime = mtime;
140 return t;
135 return t;
141 }
136 }
142
137
143 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
138 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
144 PyObject *kwds)
139 PyObject *kwds)
145 {
140 {
146 /* We do all the initialization here and not a tp_init function because
141 /* We do all the initialization here and not a tp_init function because
147 * dirstate_tuple is immutable. */
142 * dirstate_tuple is immutable. */
148 dirstateTupleObject *t;
143 dirstateTupleObject *t;
149 char state;
144 char state;
150 int size, mode, mtime;
145 int size, mode, mtime;
151 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
146 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime))
152 return NULL;
147 return NULL;
153
148
154 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
149 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
155 if (!t)
150 if (!t)
156 return NULL;
151 return NULL;
157 t->state = state;
152 t->state = state;
158 t->mode = mode;
153 t->mode = mode;
159 t->size = size;
154 t->size = size;
160 t->mtime = mtime;
155 t->mtime = mtime;
161
156
162 return (PyObject *)t;
157 return (PyObject *)t;
163 }
158 }
164
159
165 static void dirstate_tuple_dealloc(PyObject *o)
160 static void dirstate_tuple_dealloc(PyObject *o)
166 {
161 {
167 PyObject_Del(o);
162 PyObject_Del(o);
168 }
163 }
169
164
170 static Py_ssize_t dirstate_tuple_length(PyObject *o)
165 static Py_ssize_t dirstate_tuple_length(PyObject *o)
171 {
166 {
172 return 4;
167 return 4;
173 }
168 }
174
169
175 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
170 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
176 {
171 {
177 dirstateTupleObject *t = (dirstateTupleObject *)o;
172 dirstateTupleObject *t = (dirstateTupleObject *)o;
178 switch (i) {
173 switch (i) {
179 case 0:
174 case 0:
180 return PyBytes_FromStringAndSize(&t->state, 1);
175 return PyBytes_FromStringAndSize(&t->state, 1);
181 case 1:
176 case 1:
182 return PyInt_FromLong(t->mode);
177 return PyInt_FromLong(t->mode);
183 case 2:
178 case 2:
184 return PyInt_FromLong(t->size);
179 return PyInt_FromLong(t->size);
185 case 3:
180 case 3:
186 return PyInt_FromLong(t->mtime);
181 return PyInt_FromLong(t->mtime);
187 default:
182 default:
188 PyErr_SetString(PyExc_IndexError, "index out of range");
183 PyErr_SetString(PyExc_IndexError, "index out of range");
189 return NULL;
184 return NULL;
190 }
185 }
191 }
186 }
192
187
193 static PySequenceMethods dirstate_tuple_sq = {
188 static PySequenceMethods dirstate_tuple_sq = {
194 dirstate_tuple_length, /* sq_length */
189 dirstate_tuple_length, /* sq_length */
195 0, /* sq_concat */
190 0, /* sq_concat */
196 0, /* sq_repeat */
191 0, /* sq_repeat */
197 dirstate_tuple_item, /* sq_item */
192 dirstate_tuple_item, /* sq_item */
198 0, /* sq_ass_item */
193 0, /* sq_ass_item */
199 0, /* sq_contains */
194 0, /* sq_contains */
200 0, /* sq_inplace_concat */
195 0, /* sq_inplace_concat */
201 0 /* sq_inplace_repeat */
196 0 /* sq_inplace_repeat */
202 };
197 };
203
198
204 PyTypeObject dirstateTupleType = {
199 PyTypeObject dirstateTupleType = {
205 PyVarObject_HEAD_INIT(NULL, 0)
200 PyVarObject_HEAD_INIT(NULL, 0)
206 "dirstate_tuple", /* tp_name */
201 "dirstate_tuple", /* tp_name */
207 sizeof(dirstateTupleObject),/* tp_basicsize */
202 sizeof(dirstateTupleObject),/* tp_basicsize */
208 0, /* tp_itemsize */
203 0, /* tp_itemsize */
209 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
204 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
210 0, /* tp_print */
205 0, /* tp_print */
211 0, /* tp_getattr */
206 0, /* tp_getattr */
212 0, /* tp_setattr */
207 0, /* tp_setattr */
213 0, /* tp_compare */
208 0, /* tp_compare */
214 0, /* tp_repr */
209 0, /* tp_repr */
215 0, /* tp_as_number */
210 0, /* tp_as_number */
216 &dirstate_tuple_sq, /* tp_as_sequence */
211 &dirstate_tuple_sq, /* tp_as_sequence */
217 0, /* tp_as_mapping */
212 0, /* tp_as_mapping */
218 0, /* tp_hash */
213 0, /* tp_hash */
219 0, /* tp_call */
214 0, /* tp_call */
220 0, /* tp_str */
215 0, /* tp_str */
221 0, /* tp_getattro */
216 0, /* tp_getattro */
222 0, /* tp_setattro */
217 0, /* tp_setattro */
223 0, /* tp_as_buffer */
218 0, /* tp_as_buffer */
224 Py_TPFLAGS_DEFAULT, /* tp_flags */
219 Py_TPFLAGS_DEFAULT, /* tp_flags */
225 "dirstate tuple", /* tp_doc */
220 "dirstate tuple", /* tp_doc */
226 0, /* tp_traverse */
221 0, /* tp_traverse */
227 0, /* tp_clear */
222 0, /* tp_clear */
228 0, /* tp_richcompare */
223 0, /* tp_richcompare */
229 0, /* tp_weaklistoffset */
224 0, /* tp_weaklistoffset */
230 0, /* tp_iter */
225 0, /* tp_iter */
231 0, /* tp_iternext */
226 0, /* tp_iternext */
232 0, /* tp_methods */
227 0, /* tp_methods */
233 0, /* tp_members */
228 0, /* tp_members */
234 0, /* tp_getset */
229 0, /* tp_getset */
235 0, /* tp_base */
230 0, /* tp_base */
236 0, /* tp_dict */
231 0, /* tp_dict */
237 0, /* tp_descr_get */
232 0, /* tp_descr_get */
238 0, /* tp_descr_set */
233 0, /* tp_descr_set */
239 0, /* tp_dictoffset */
234 0, /* tp_dictoffset */
240 0, /* tp_init */
235 0, /* tp_init */
241 0, /* tp_alloc */
236 0, /* tp_alloc */
242 dirstate_tuple_new, /* tp_new */
237 dirstate_tuple_new, /* tp_new */
243 };
238 };
244
239
245 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
240 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
246 {
241 {
247 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
242 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
248 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
243 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
249 char state, *cur, *str, *cpos;
244 char state, *cur, *str, *cpos;
250 int mode, size, mtime;
245 int mode, size, mtime;
251 unsigned int flen, len, pos = 40;
246 unsigned int flen, len, pos = 40;
252 int readlen;
247 int readlen;
253
248
254 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
249 if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
255 &PyDict_Type, &dmap,
250 &PyDict_Type, &dmap,
256 &PyDict_Type, &cmap,
251 &PyDict_Type, &cmap,
257 &str, &readlen))
252 &str, &readlen))
258 goto quit;
253 goto quit;
259
254
260 len = readlen;
255 len = readlen;
261
256
262 /* read parents */
257 /* read parents */
263 if (len < 40) {
258 if (len < 40) {
264 PyErr_SetString(
259 PyErr_SetString(
265 PyExc_ValueError, "too little data for parents");
260 PyExc_ValueError, "too little data for parents");
266 goto quit;
261 goto quit;
267 }
262 }
268
263
269 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
264 parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
270 if (!parents)
265 if (!parents)
271 goto quit;
266 goto quit;
272
267
273 /* read filenames */
268 /* read filenames */
274 while (pos >= 40 && pos < len) {
269 while (pos >= 40 && pos < len) {
275 if (pos + 17 > len) {
270 if (pos + 17 > len) {
276 PyErr_SetString(PyExc_ValueError,
271 PyErr_SetString(PyExc_ValueError,
277 "overflow in dirstate");
272 "overflow in dirstate");
278 goto quit;
273 goto quit;
279 }
274 }
280 cur = str + pos;
275 cur = str + pos;
281 /* unpack header */
276 /* unpack header */
282 state = *cur;
277 state = *cur;
283 mode = getbe32(cur + 1);
278 mode = getbe32(cur + 1);
284 size = getbe32(cur + 5);
279 size = getbe32(cur + 5);
285 mtime = getbe32(cur + 9);
280 mtime = getbe32(cur + 9);
286 flen = getbe32(cur + 13);
281 flen = getbe32(cur + 13);
287 pos += 17;
282 pos += 17;
288 cur += 17;
283 cur += 17;
289 if (flen > len - pos) {
284 if (flen > len - pos) {
290 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
285 PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
291 goto quit;
286 goto quit;
292 }
287 }
293
288
294 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
289 entry = (PyObject *)make_dirstate_tuple(state, mode, size,
295 mtime);
290 mtime);
296 cpos = memchr(cur, 0, flen);
291 cpos = memchr(cur, 0, flen);
297 if (cpos) {
292 if (cpos) {
298 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
293 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
299 cname = PyBytes_FromStringAndSize(cpos + 1,
294 cname = PyBytes_FromStringAndSize(cpos + 1,
300 flen - (cpos - cur) - 1);
295 flen - (cpos - cur) - 1);
301 if (!fname || !cname ||
296 if (!fname || !cname ||
302 PyDict_SetItem(cmap, fname, cname) == -1 ||
297 PyDict_SetItem(cmap, fname, cname) == -1 ||
303 PyDict_SetItem(dmap, fname, entry) == -1)
298 PyDict_SetItem(dmap, fname, entry) == -1)
304 goto quit;
299 goto quit;
305 Py_DECREF(cname);
300 Py_DECREF(cname);
306 } else {
301 } else {
307 fname = PyBytes_FromStringAndSize(cur, flen);
302 fname = PyBytes_FromStringAndSize(cur, flen);
308 if (!fname ||
303 if (!fname ||
309 PyDict_SetItem(dmap, fname, entry) == -1)
304 PyDict_SetItem(dmap, fname, entry) == -1)
310 goto quit;
305 goto quit;
311 }
306 }
312 Py_DECREF(fname);
307 Py_DECREF(fname);
313 Py_DECREF(entry);
308 Py_DECREF(entry);
314 fname = cname = entry = NULL;
309 fname = cname = entry = NULL;
315 pos += flen;
310 pos += flen;
316 }
311 }
317
312
318 ret = parents;
313 ret = parents;
319 Py_INCREF(ret);
314 Py_INCREF(ret);
320 quit:
315 quit:
321 Py_XDECREF(fname);
316 Py_XDECREF(fname);
322 Py_XDECREF(cname);
317 Py_XDECREF(cname);
323 Py_XDECREF(entry);
318 Py_XDECREF(entry);
324 Py_XDECREF(parents);
319 Py_XDECREF(parents);
325 return ret;
320 return ret;
326 }
321 }
327
322
328 /*
323 /*
329 * Build a set of non-normal and other parent entries from the dirstate dmap
324 * Build a set of non-normal and other parent entries from the dirstate dmap
330 */
325 */
331 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args) {
326 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args) {
332 PyObject *dmap, *fname, *v;
327 PyObject *dmap, *fname, *v;
333 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
328 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
334 Py_ssize_t pos;
329 Py_ssize_t pos;
335
330
336 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
331 if (!PyArg_ParseTuple(args, "O!:nonnormalentries",
337 &PyDict_Type, &dmap))
332 &PyDict_Type, &dmap))
338 goto bail;
333 goto bail;
339
334
340 nonnset = PySet_New(NULL);
335 nonnset = PySet_New(NULL);
341 if (nonnset == NULL)
336 if (nonnset == NULL)
342 goto bail;
337 goto bail;
343
338
344 otherpset = PySet_New(NULL);
339 otherpset = PySet_New(NULL);
345 if (otherpset == NULL)
340 if (otherpset == NULL)
346 goto bail;
341 goto bail;
347
342
348 pos = 0;
343 pos = 0;
349 while (PyDict_Next(dmap, &pos, &fname, &v)) {
344 while (PyDict_Next(dmap, &pos, &fname, &v)) {
350 dirstateTupleObject *t;
345 dirstateTupleObject *t;
351 if (!dirstate_tuple_check(v)) {
346 if (!dirstate_tuple_check(v)) {
352 PyErr_SetString(PyExc_TypeError,
347 PyErr_SetString(PyExc_TypeError,
353 "expected a dirstate tuple");
348 "expected a dirstate tuple");
354 goto bail;
349 goto bail;
355 }
350 }
356 t = (dirstateTupleObject *)v;
351 t = (dirstateTupleObject *)v;
357
352
358 if (t->state == 'n' && t->size == -2) {
353 if (t->state == 'n' && t->size == -2) {
359 if (PySet_Add(otherpset, fname) == -1) {
354 if (PySet_Add(otherpset, fname) == -1) {
360 goto bail;
355 goto bail;
361 }
356 }
362 }
357 }
363
358
364 if (t->state == 'n' && t->mtime != -1)
359 if (t->state == 'n' && t->mtime != -1)
365 continue;
360 continue;
366 if (PySet_Add(nonnset, fname) == -1)
361 if (PySet_Add(nonnset, fname) == -1)
367 goto bail;
362 goto bail;
368 }
363 }
369
364
370 result = Py_BuildValue("(OO)", nonnset, otherpset);
365 result = Py_BuildValue("(OO)", nonnset, otherpset);
371 if (result == NULL)
366 if (result == NULL)
372 goto bail;
367 goto bail;
373 Py_DECREF(nonnset);
368 Py_DECREF(nonnset);
374 Py_DECREF(otherpset);
369 Py_DECREF(otherpset);
375 return result;
370 return result;
376 bail:
371 bail:
377 Py_XDECREF(nonnset);
372 Py_XDECREF(nonnset);
378 Py_XDECREF(otherpset);
373 Py_XDECREF(otherpset);
379 Py_XDECREF(result);
374 Py_XDECREF(result);
380 return NULL;
375 return NULL;
381 }
376 }
382
377
383 /*
378 /*
384 * Efficiently pack a dirstate object into its on-disk format.
379 * Efficiently pack a dirstate object into its on-disk format.
385 */
380 */
386 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
381 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
387 {
382 {
388 PyObject *packobj = NULL;
383 PyObject *packobj = NULL;
389 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
384 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
390 Py_ssize_t nbytes, pos, l;
385 Py_ssize_t nbytes, pos, l;
391 PyObject *k, *v = NULL, *pn;
386 PyObject *k, *v = NULL, *pn;
392 char *p, *s;
387 char *p, *s;
393 int now;
388 int now;
394
389
395 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
390 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
396 &PyDict_Type, &map, &PyDict_Type, &copymap,
391 &PyDict_Type, &map, &PyDict_Type, &copymap,
397 &pl, &now))
392 &pl, &now))
398 return NULL;
393 return NULL;
399
394
400 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
395 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
401 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
396 PyErr_SetString(PyExc_TypeError, "expected 2-element sequence");
402 return NULL;
397 return NULL;
403 }
398 }
404
399
405 /* Figure out how much we need to allocate. */
400 /* Figure out how much we need to allocate. */
406 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
401 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
407 PyObject *c;
402 PyObject *c;
408 if (!PyBytes_Check(k)) {
403 if (!PyBytes_Check(k)) {
409 PyErr_SetString(PyExc_TypeError, "expected string key");
404 PyErr_SetString(PyExc_TypeError, "expected string key");
410 goto bail;
405 goto bail;
411 }
406 }
412 nbytes += PyBytes_GET_SIZE(k) + 17;
407 nbytes += PyBytes_GET_SIZE(k) + 17;
413 c = PyDict_GetItem(copymap, k);
408 c = PyDict_GetItem(copymap, k);
414 if (c) {
409 if (c) {
415 if (!PyBytes_Check(c)) {
410 if (!PyBytes_Check(c)) {
416 PyErr_SetString(PyExc_TypeError,
411 PyErr_SetString(PyExc_TypeError,
417 "expected string key");
412 "expected string key");
418 goto bail;
413 goto bail;
419 }
414 }
420 nbytes += PyBytes_GET_SIZE(c) + 1;
415 nbytes += PyBytes_GET_SIZE(c) + 1;
421 }
416 }
422 }
417 }
423
418
424 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
419 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
425 if (packobj == NULL)
420 if (packobj == NULL)
426 goto bail;
421 goto bail;
427
422
428 p = PyBytes_AS_STRING(packobj);
423 p = PyBytes_AS_STRING(packobj);
429
424
430 pn = PySequence_ITEM(pl, 0);
425 pn = PySequence_ITEM(pl, 0);
431 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
426 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
432 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
427 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
433 goto bail;
428 goto bail;
434 }
429 }
435 memcpy(p, s, l);
430 memcpy(p, s, l);
436 p += 20;
431 p += 20;
437 pn = PySequence_ITEM(pl, 1);
432 pn = PySequence_ITEM(pl, 1);
438 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
433 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
439 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
434 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
440 goto bail;
435 goto bail;
441 }
436 }
442 memcpy(p, s, l);
437 memcpy(p, s, l);
443 p += 20;
438 p += 20;
444
439
445 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
440 for (pos = 0; PyDict_Next(map, &pos, &k, &v); ) {
446 dirstateTupleObject *tuple;
441 dirstateTupleObject *tuple;
447 char state;
442 char state;
448 int mode, size, mtime;
443 int mode, size, mtime;
449 Py_ssize_t len, l;
444 Py_ssize_t len, l;
450 PyObject *o;
445 PyObject *o;
451 char *t;
446 char *t;
452
447
453 if (!dirstate_tuple_check(v)) {
448 if (!dirstate_tuple_check(v)) {
454 PyErr_SetString(PyExc_TypeError,
449 PyErr_SetString(PyExc_TypeError,
455 "expected a dirstate tuple");
450 "expected a dirstate tuple");
456 goto bail;
451 goto bail;
457 }
452 }
458 tuple = (dirstateTupleObject *)v;
453 tuple = (dirstateTupleObject *)v;
459
454
460 state = tuple->state;
455 state = tuple->state;
461 mode = tuple->mode;
456 mode = tuple->mode;
462 size = tuple->size;
457 size = tuple->size;
463 mtime = tuple->mtime;
458 mtime = tuple->mtime;
464 if (state == 'n' && mtime == now) {
459 if (state == 'n' && mtime == now) {
465 /* See pure/parsers.py:pack_dirstate for why we do
460 /* See pure/parsers.py:pack_dirstate for why we do
466 * this. */
461 * this. */
467 mtime = -1;
462 mtime = -1;
468 mtime_unset = (PyObject *)make_dirstate_tuple(
463 mtime_unset = (PyObject *)make_dirstate_tuple(
469 state, mode, size, mtime);
464 state, mode, size, mtime);
470 if (!mtime_unset)
465 if (!mtime_unset)
471 goto bail;
466 goto bail;
472 if (PyDict_SetItem(map, k, mtime_unset) == -1)
467 if (PyDict_SetItem(map, k, mtime_unset) == -1)
473 goto bail;
468 goto bail;
474 Py_DECREF(mtime_unset);
469 Py_DECREF(mtime_unset);
475 mtime_unset = NULL;
470 mtime_unset = NULL;
476 }
471 }
477 *p++ = state;
472 *p++ = state;
478 putbe32((uint32_t)mode, p);
473 putbe32((uint32_t)mode, p);
479 putbe32((uint32_t)size, p + 4);
474 putbe32((uint32_t)size, p + 4);
480 putbe32((uint32_t)mtime, p + 8);
475 putbe32((uint32_t)mtime, p + 8);
481 t = p + 12;
476 t = p + 12;
482 p += 16;
477 p += 16;
483 len = PyBytes_GET_SIZE(k);
478 len = PyBytes_GET_SIZE(k);
484 memcpy(p, PyBytes_AS_STRING(k), len);
479 memcpy(p, PyBytes_AS_STRING(k), len);
485 p += len;
480 p += len;
486 o = PyDict_GetItem(copymap, k);
481 o = PyDict_GetItem(copymap, k);
487 if (o) {
482 if (o) {
488 *p++ = '\0';
483 *p++ = '\0';
489 l = PyBytes_GET_SIZE(o);
484 l = PyBytes_GET_SIZE(o);
490 memcpy(p, PyBytes_AS_STRING(o), l);
485 memcpy(p, PyBytes_AS_STRING(o), l);
491 p += l;
486 p += l;
492 len += l + 1;
487 len += l + 1;
493 }
488 }
494 putbe32((uint32_t)len, t);
489 putbe32((uint32_t)len, t);
495 }
490 }
496
491
497 pos = p - PyBytes_AS_STRING(packobj);
492 pos = p - PyBytes_AS_STRING(packobj);
498 if (pos != nbytes) {
493 if (pos != nbytes) {
499 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
494 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
500 (long)pos, (long)nbytes);
495 (long)pos, (long)nbytes);
501 goto bail;
496 goto bail;
502 }
497 }
503
498
504 return packobj;
499 return packobj;
505 bail:
500 bail:
506 Py_XDECREF(mtime_unset);
501 Py_XDECREF(mtime_unset);
507 Py_XDECREF(packobj);
502 Py_XDECREF(packobj);
508 Py_XDECREF(v);
503 Py_XDECREF(v);
509 return NULL;
504 return NULL;
510 }
505 }
511
506
512 #define BUMPED_FIX 1
507 #define BUMPED_FIX 1
513 #define USING_SHA_256 2
508 #define USING_SHA_256 2
514 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
509 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
515
510
516 static PyObject *readshas(
511 static PyObject *readshas(
517 const char *source, unsigned char num, Py_ssize_t hashwidth)
512 const char *source, unsigned char num, Py_ssize_t hashwidth)
518 {
513 {
519 int i;
514 int i;
520 PyObject *list = PyTuple_New(num);
515 PyObject *list = PyTuple_New(num);
521 if (list == NULL) {
516 if (list == NULL) {
522 return NULL;
517 return NULL;
523 }
518 }
524 for (i = 0; i < num; i++) {
519 for (i = 0; i < num; i++) {
525 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
520 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
526 if (hash == NULL) {
521 if (hash == NULL) {
527 Py_DECREF(list);
522 Py_DECREF(list);
528 return NULL;
523 return NULL;
529 }
524 }
530 PyTuple_SET_ITEM(list, i, hash);
525 PyTuple_SET_ITEM(list, i, hash);
531 source += hashwidth;
526 source += hashwidth;
532 }
527 }
533 return list;
528 return list;
534 }
529 }
535
530
536 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
531 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
537 uint32_t *msize)
532 uint32_t *msize)
538 {
533 {
539 const char *data = databegin;
534 const char *data = databegin;
540 const char *meta;
535 const char *meta;
541
536
542 double mtime;
537 double mtime;
543 int16_t tz;
538 int16_t tz;
544 uint16_t flags;
539 uint16_t flags;
545 unsigned char nsuccs, nparents, nmetadata;
540 unsigned char nsuccs, nparents, nmetadata;
546 Py_ssize_t hashwidth = 20;
541 Py_ssize_t hashwidth = 20;
547
542
548 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
543 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
549 PyObject *metadata = NULL, *ret = NULL;
544 PyObject *metadata = NULL, *ret = NULL;
550 int i;
545 int i;
551
546
552 if (data + FM1_HEADER_SIZE > dataend) {
547 if (data + FM1_HEADER_SIZE > dataend) {
553 goto overflow;
548 goto overflow;
554 }
549 }
555
550
556 *msize = getbe32(data);
551 *msize = getbe32(data);
557 data += 4;
552 data += 4;
558 mtime = getbefloat64(data);
553 mtime = getbefloat64(data);
559 data += 8;
554 data += 8;
560 tz = getbeint16(data);
555 tz = getbeint16(data);
561 data += 2;
556 data += 2;
562 flags = getbeuint16(data);
557 flags = getbeuint16(data);
563 data += 2;
558 data += 2;
564
559
565 if (flags & USING_SHA_256) {
560 if (flags & USING_SHA_256) {
566 hashwidth = 32;
561 hashwidth = 32;
567 }
562 }
568
563
569 nsuccs = (unsigned char)(*data++);
564 nsuccs = (unsigned char)(*data++);
570 nparents = (unsigned char)(*data++);
565 nparents = (unsigned char)(*data++);
571 nmetadata = (unsigned char)(*data++);
566 nmetadata = (unsigned char)(*data++);
572
567
573 if (databegin + *msize > dataend) {
568 if (databegin + *msize > dataend) {
574 goto overflow;
569 goto overflow;
575 }
570 }
576 dataend = databegin + *msize; /* narrow down to marker size */
571 dataend = databegin + *msize; /* narrow down to marker size */
577
572
578 if (data + hashwidth > dataend) {
573 if (data + hashwidth > dataend) {
579 goto overflow;
574 goto overflow;
580 }
575 }
581 prec = PyBytes_FromStringAndSize(data, hashwidth);
576 prec = PyBytes_FromStringAndSize(data, hashwidth);
582 data += hashwidth;
577 data += hashwidth;
583 if (prec == NULL) {
578 if (prec == NULL) {
584 goto bail;
579 goto bail;
585 }
580 }
586
581
587 if (data + nsuccs * hashwidth > dataend) {
582 if (data + nsuccs * hashwidth > dataend) {
588 goto overflow;
583 goto overflow;
589 }
584 }
590 succs = readshas(data, nsuccs, hashwidth);
585 succs = readshas(data, nsuccs, hashwidth);
591 if (succs == NULL) {
586 if (succs == NULL) {
592 goto bail;
587 goto bail;
593 }
588 }
594 data += nsuccs * hashwidth;
589 data += nsuccs * hashwidth;
595
590
596 if (nparents == 1 || nparents == 2) {
591 if (nparents == 1 || nparents == 2) {
597 if (data + nparents * hashwidth > dataend) {
592 if (data + nparents * hashwidth > dataend) {
598 goto overflow;
593 goto overflow;
599 }
594 }
600 parents = readshas(data, nparents, hashwidth);
595 parents = readshas(data, nparents, hashwidth);
601 if (parents == NULL) {
596 if (parents == NULL) {
602 goto bail;
597 goto bail;
603 }
598 }
604 data += nparents * hashwidth;
599 data += nparents * hashwidth;
605 } else {
600 } else {
606 parents = Py_None;
601 parents = Py_None;
607 Py_INCREF(parents);
602 Py_INCREF(parents);
608 }
603 }
609
604
610 if (data + 2 * nmetadata > dataend) {
605 if (data + 2 * nmetadata > dataend) {
611 goto overflow;
606 goto overflow;
612 }
607 }
613 meta = data + (2 * nmetadata);
608 meta = data + (2 * nmetadata);
614 metadata = PyTuple_New(nmetadata);
609 metadata = PyTuple_New(nmetadata);
615 if (metadata == NULL) {
610 if (metadata == NULL) {
616 goto bail;
611 goto bail;
617 }
612 }
618 for (i = 0; i < nmetadata; i++) {
613 for (i = 0; i < nmetadata; i++) {
619 PyObject *tmp, *left = NULL, *right = NULL;
614 PyObject *tmp, *left = NULL, *right = NULL;
620 Py_ssize_t leftsize = (unsigned char)(*data++);
615 Py_ssize_t leftsize = (unsigned char)(*data++);
621 Py_ssize_t rightsize = (unsigned char)(*data++);
616 Py_ssize_t rightsize = (unsigned char)(*data++);
622 if (meta + leftsize + rightsize > dataend) {
617 if (meta + leftsize + rightsize > dataend) {
623 goto overflow;
618 goto overflow;
624 }
619 }
625 left = PyBytes_FromStringAndSize(meta, leftsize);
620 left = PyBytes_FromStringAndSize(meta, leftsize);
626 meta += leftsize;
621 meta += leftsize;
627 right = PyBytes_FromStringAndSize(meta, rightsize);
622 right = PyBytes_FromStringAndSize(meta, rightsize);
628 meta += rightsize;
623 meta += rightsize;
629 tmp = PyTuple_New(2);
624 tmp = PyTuple_New(2);
630 if (!left || !right || !tmp) {
625 if (!left || !right || !tmp) {
631 Py_XDECREF(left);
626 Py_XDECREF(left);
632 Py_XDECREF(right);
627 Py_XDECREF(right);
633 Py_XDECREF(tmp);
628 Py_XDECREF(tmp);
634 goto bail;
629 goto bail;
635 }
630 }
636 PyTuple_SET_ITEM(tmp, 0, left);
631 PyTuple_SET_ITEM(tmp, 0, left);
637 PyTuple_SET_ITEM(tmp, 1, right);
632 PyTuple_SET_ITEM(tmp, 1, right);
638 PyTuple_SET_ITEM(metadata, i, tmp);
633 PyTuple_SET_ITEM(metadata, i, tmp);
639 }
634 }
640 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
635 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
641 metadata, mtime, (int)tz * 60, parents);
636 metadata, mtime, (int)tz * 60, parents);
642 goto bail; /* return successfully */
637 goto bail; /* return successfully */
643
638
644 overflow:
639 overflow:
645 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
640 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
646 bail:
641 bail:
647 Py_XDECREF(prec);
642 Py_XDECREF(prec);
648 Py_XDECREF(succs);
643 Py_XDECREF(succs);
649 Py_XDECREF(metadata);
644 Py_XDECREF(metadata);
650 Py_XDECREF(parents);
645 Py_XDECREF(parents);
651 return ret;
646 return ret;
652 }
647 }
653
648
654
649
655 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
650 static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
656 const char *data, *dataend;
651 const char *data, *dataend;
657 int datalen;
652 int datalen;
658 Py_ssize_t offset, stop;
653 Py_ssize_t offset, stop;
659 PyObject *markers = NULL;
654 PyObject *markers = NULL;
660
655
661 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
656 if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
662 return NULL;
657 return NULL;
663 }
658 }
664 dataend = data + datalen;
659 dataend = data + datalen;
665 data += offset;
660 data += offset;
666 markers = PyList_New(0);
661 markers = PyList_New(0);
667 if (!markers) {
662 if (!markers) {
668 return NULL;
663 return NULL;
669 }
664 }
670 while (offset < stop) {
665 while (offset < stop) {
671 uint32_t msize;
666 uint32_t msize;
672 int error;
667 int error;
673 PyObject *record = fm1readmarker(data, dataend, &msize);
668 PyObject *record = fm1readmarker(data, dataend, &msize);
674 if (!record) {
669 if (!record) {
675 goto bail;
670 goto bail;
676 }
671 }
677 error = PyList_Append(markers, record);
672 error = PyList_Append(markers, record);
678 Py_DECREF(record);
673 Py_DECREF(record);
679 if (error) {
674 if (error) {
680 goto bail;
675 goto bail;
681 }
676 }
682 data += msize;
677 data += msize;
683 offset += msize;
678 offset += msize;
684 }
679 }
685 return markers;
680 return markers;
686 bail:
681 bail:
687 Py_DECREF(markers);
682 Py_DECREF(markers);
688 return NULL;
683 return NULL;
689 }
684 }
690
685
691 static char parsers_doc[] = "Efficient content parsing.";
686 static char parsers_doc[] = "Efficient content parsing.";
692
687
693 PyObject *encodedir(PyObject *self, PyObject *args);
688 PyObject *encodedir(PyObject *self, PyObject *args);
694 PyObject *pathencode(PyObject *self, PyObject *args);
689 PyObject *pathencode(PyObject *self, PyObject *args);
695 PyObject *lowerencode(PyObject *self, PyObject *args);
690 PyObject *lowerencode(PyObject *self, PyObject *args);
696 PyObject *parse_index2(PyObject *self, PyObject *args);
691 PyObject *parse_index2(PyObject *self, PyObject *args);
697
692
698 static PyMethodDef methods[] = {
693 static PyMethodDef methods[] = {
699 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
694 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
700 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
695 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
701 "create a set containing non-normal and other parent entries of given "
696 "create a set containing non-normal and other parent entries of given "
702 "dirstate\n"},
697 "dirstate\n"},
703 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
698 {"parse_manifest", parse_manifest, METH_VARARGS, "parse a manifest\n"},
704 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
699 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
705 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
700 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
706 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
701 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
707 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
702 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
708 {"dict_new_presized", dict_new_presized, METH_VARARGS,
703 {"dict_new_presized", dict_new_presized, METH_VARARGS,
709 "construct a dict with an expected size\n"},
704 "construct a dict with an expected size\n"},
710 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
705 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
711 "make file foldmap\n"},
706 "make file foldmap\n"},
712 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
707 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
713 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
708 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
714 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
709 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
715 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
710 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
716 "parse v1 obsolete markers\n"},
711 "parse v1 obsolete markers\n"},
717 {NULL, NULL}
712 {NULL, NULL}
718 };
713 };
719
714
720 void dirs_module_init(PyObject *mod);
715 void dirs_module_init(PyObject *mod);
721 void manifest_module_init(PyObject *mod);
716 void manifest_module_init(PyObject *mod);
722 void revlog_module_init(PyObject *mod);
717 void revlog_module_init(PyObject *mod);
723
718
724 static const int version = 1;
719 static const int version = 1;
725
720
726 static void module_init(PyObject *mod)
721 static void module_init(PyObject *mod)
727 {
722 {
728 PyModule_AddIntConstant(mod, "version", version);
723 PyModule_AddIntConstant(mod, "version", version);
729
724
730 /* This module constant has two purposes. First, it lets us unit test
725 /* This module constant has two purposes. First, it lets us unit test
731 * the ImportError raised without hard-coding any error text. This
726 * the ImportError raised without hard-coding any error text. This
732 * means we can change the text in the future without breaking tests,
727 * means we can change the text in the future without breaking tests,
733 * even across changesets without a recompile. Second, its presence
728 * even across changesets without a recompile. Second, its presence
734 * can be used to determine whether the version-checking logic is
729 * can be used to determine whether the version-checking logic is
735 * present, which also helps in testing across changesets without a
730 * present, which also helps in testing across changesets without a
736 * recompile. Note that this means the pure-Python version of parsers
731 * recompile. Note that this means the pure-Python version of parsers
737 * should not have this module constant. */
732 * should not have this module constant. */
738 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
733 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
739
734
740 dirs_module_init(mod);
735 dirs_module_init(mod);
741 manifest_module_init(mod);
736 manifest_module_init(mod);
742 revlog_module_init(mod);
737 revlog_module_init(mod);
743
738
744 if (PyType_Ready(&dirstateTupleType) < 0)
739 if (PyType_Ready(&dirstateTupleType) < 0)
745 return;
740 return;
746 Py_INCREF(&dirstateTupleType);
741 Py_INCREF(&dirstateTupleType);
747 PyModule_AddObject(mod, "dirstatetuple",
742 PyModule_AddObject(mod, "dirstatetuple",
748 (PyObject *)&dirstateTupleType);
743 (PyObject *)&dirstateTupleType);
749 }
744 }
750
745
751 static int check_python_version(void)
746 static int check_python_version(void)
752 {
747 {
753 PyObject *sys = PyImport_ImportModule("sys"), *ver;
748 PyObject *sys = PyImport_ImportModule("sys"), *ver;
754 long hexversion;
749 long hexversion;
755 if (!sys)
750 if (!sys)
756 return -1;
751 return -1;
757 ver = PyObject_GetAttrString(sys, "hexversion");
752 ver = PyObject_GetAttrString(sys, "hexversion");
758 Py_DECREF(sys);
753 Py_DECREF(sys);
759 if (!ver)
754 if (!ver)
760 return -1;
755 return -1;
761 hexversion = PyInt_AsLong(ver);
756 hexversion = PyInt_AsLong(ver);
762 Py_DECREF(ver);
757 Py_DECREF(ver);
763 /* sys.hexversion is a 32-bit number by default, so the -1 case
758 /* sys.hexversion is a 32-bit number by default, so the -1 case
764 * should only occur in unusual circumstances (e.g. if sys.hexversion
759 * should only occur in unusual circumstances (e.g. if sys.hexversion
765 * is manually set to an invalid value). */
760 * is manually set to an invalid value). */
766 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
761 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
767 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
762 PyErr_Format(PyExc_ImportError, "%s: The Mercurial extension "
768 "modules were compiled with Python " PY_VERSION ", but "
763 "modules were compiled with Python " PY_VERSION ", but "
769 "Mercurial is currently using Python with sys.hexversion=%ld: "
764 "Mercurial is currently using Python with sys.hexversion=%ld: "
770 "Python %s\n at: %s", versionerrortext, hexversion,
765 "Python %s\n at: %s", versionerrortext, hexversion,
771 Py_GetVersion(), Py_GetProgramFullPath());
766 Py_GetVersion(), Py_GetProgramFullPath());
772 return -1;
767 return -1;
773 }
768 }
774 return 0;
769 return 0;
775 }
770 }
776
771
777 #ifdef IS_PY3K
772 #ifdef IS_PY3K
778 static struct PyModuleDef parsers_module = {
773 static struct PyModuleDef parsers_module = {
779 PyModuleDef_HEAD_INIT,
774 PyModuleDef_HEAD_INIT,
780 "parsers",
775 "parsers",
781 parsers_doc,
776 parsers_doc,
782 -1,
777 -1,
783 methods
778 methods
784 };
779 };
785
780
786 PyMODINIT_FUNC PyInit_parsers(void)
781 PyMODINIT_FUNC PyInit_parsers(void)
787 {
782 {
788 PyObject *mod;
783 PyObject *mod;
789
784
790 if (check_python_version() == -1)
785 if (check_python_version() == -1)
791 return NULL;
786 return NULL;
792 mod = PyModule_Create(&parsers_module);
787 mod = PyModule_Create(&parsers_module);
793 module_init(mod);
788 module_init(mod);
794 return mod;
789 return mod;
795 }
790 }
796 #else
791 #else
797 PyMODINIT_FUNC initparsers(void)
792 PyMODINIT_FUNC initparsers(void)
798 {
793 {
799 PyObject *mod;
794 PyObject *mod;
800
795
801 if (check_python_version() == -1)
796 if (check_python_version() == -1)
802 return;
797 return;
803 mod = Py_InitModule3("parsers", methods, parsers_doc);
798 mod = Py_InitModule3("parsers", methods, parsers_doc);
804 module_init(mod);
799 module_init(mod);
805 }
800 }
806 #endif
801 #endif
@@ -1,2089 +1,2090 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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 #include <Python.h>
10 #include <Python.h>
11 #include <assert.h>
11 #include <assert.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 "charencode.h"
16 #include "util.h"
17 #include "util.h"
17 #include "bitmanipulation.h"
18 #include "bitmanipulation.h"
18
19
19 #ifdef IS_PY3K
20 #ifdef IS_PY3K
20 /* 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
21 * 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
22 * supported and proper types are used in the extensions themselves. */
23 * supported and proper types are used in the extensions themselves. */
23 #define PyInt_Check PyLong_Check
24 #define PyInt_Check PyLong_Check
24 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AS_LONG PyLong_AS_LONG
27 #define PyInt_AsLong PyLong_AsLong
28 #define PyInt_AsLong PyLong_AsLong
28 #endif
29 #endif
29
30
30 /*
31 /*
31 * A base-16 trie for fast node->rev mapping.
32 * A base-16 trie for fast node->rev mapping.
32 *
33 *
33 * Positive value is index of the next node in the trie
34 * Positive value is index of the next node in the trie
34 * Negative value is a leaf: -(rev + 1)
35 * Negative value is a leaf: -(rev + 1)
35 * Zero is empty
36 * Zero is empty
36 */
37 */
37 typedef struct {
38 typedef struct {
38 int children[16];
39 int children[16];
39 } nodetree;
40 } nodetree;
40
41
41 /*
42 /*
42 * This class has two behaviors.
43 * This class has two behaviors.
43 *
44 *
44 * When used in a list-like way (with integer keys), we decode an
45 * When used in a list-like way (with integer keys), we decode an
45 * entry in a RevlogNG index file on demand. Our last entry is a
46 * entry in a RevlogNG index file on demand. Our last entry is a
46 * sentinel, always a nullid. We have limited support for
47 * sentinel, always a nullid. We have limited support for
47 * integer-keyed insert and delete, only at elements right before the
48 * integer-keyed insert and delete, only at elements right before the
48 * sentinel.
49 * sentinel.
49 *
50 *
50 * With string keys, we lazily perform a reverse mapping from node to
51 * With string keys, we lazily perform a reverse mapping from node to
51 * rev, using a base-16 trie.
52 * rev, using a base-16 trie.
52 */
53 */
53 typedef struct {
54 typedef struct {
54 PyObject_HEAD
55 PyObject_HEAD
55 /* Type-specific fields go here. */
56 /* Type-specific fields go here. */
56 PyObject *data; /* raw bytes of index */
57 PyObject *data; /* raw bytes of index */
57 Py_buffer buf; /* buffer of data */
58 Py_buffer buf; /* buffer of data */
58 PyObject **cache; /* cached tuples */
59 PyObject **cache; /* cached tuples */
59 const char **offsets; /* populated on demand */
60 const char **offsets; /* populated on demand */
60 Py_ssize_t raw_length; /* original number of elements */
61 Py_ssize_t raw_length; /* original number of elements */
61 Py_ssize_t length; /* current number of elements */
62 Py_ssize_t length; /* current number of elements */
62 PyObject *added; /* populated on demand */
63 PyObject *added; /* populated on demand */
63 PyObject *headrevs; /* cache, invalidated on changes */
64 PyObject *headrevs; /* cache, invalidated on changes */
64 PyObject *filteredrevs;/* filtered revs set */
65 PyObject *filteredrevs;/* filtered revs set */
65 nodetree *nt; /* base-16 trie */
66 nodetree *nt; /* base-16 trie */
66 unsigned ntlength; /* # nodes in use */
67 unsigned ntlength; /* # nodes in use */
67 unsigned ntcapacity; /* # nodes allocated */
68 unsigned ntcapacity; /* # nodes allocated */
68 int ntdepth; /* maximum depth of tree */
69 int ntdepth; /* maximum depth of tree */
69 int ntsplits; /* # splits performed */
70 int ntsplits; /* # splits performed */
70 int ntrev; /* last rev scanned */
71 int ntrev; /* last rev scanned */
71 int ntlookups; /* # lookups */
72 int ntlookups; /* # lookups */
72 int ntmisses; /* # lookups that miss the cache */
73 int ntmisses; /* # lookups that miss the cache */
73 int inlined;
74 int inlined;
74 } indexObject;
75 } indexObject;
75
76
76 static Py_ssize_t index_length(const indexObject *self)
77 static Py_ssize_t index_length(const indexObject *self)
77 {
78 {
78 if (self->added == NULL)
79 if (self->added == NULL)
79 return self->length;
80 return self->length;
80 return self->length + PyList_GET_SIZE(self->added);
81 return self->length + PyList_GET_SIZE(self->added);
81 }
82 }
82
83
83 static PyObject *nullentry;
84 static PyObject *nullentry;
84 static const char nullid[20];
85 static const char nullid[20];
85
86
86 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
87 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
87
88
88 #if LONG_MAX == 0x7fffffffL
89 #if LONG_MAX == 0x7fffffffL
89 static char *tuple_format = "Kiiiiiis#";
90 static char *tuple_format = "Kiiiiiis#";
90 #else
91 #else
91 static char *tuple_format = "kiiiiiis#";
92 static char *tuple_format = "kiiiiiis#";
92 #endif
93 #endif
93
94
94 /* A RevlogNG v1 index entry is 64 bytes long. */
95 /* A RevlogNG v1 index entry is 64 bytes long. */
95 static const long v1_hdrsize = 64;
96 static const long v1_hdrsize = 64;
96
97
97 /*
98 /*
98 * Return a pointer to the beginning of a RevlogNG record.
99 * Return a pointer to the beginning of a RevlogNG record.
99 */
100 */
100 static const char *index_deref(indexObject *self, Py_ssize_t pos)
101 static const char *index_deref(indexObject *self, Py_ssize_t pos)
101 {
102 {
102 if (self->inlined && pos > 0) {
103 if (self->inlined && pos > 0) {
103 if (self->offsets == NULL) {
104 if (self->offsets == NULL) {
104 self->offsets = PyMem_Malloc(self->raw_length *
105 self->offsets = PyMem_Malloc(self->raw_length *
105 sizeof(*self->offsets));
106 sizeof(*self->offsets));
106 if (self->offsets == NULL)
107 if (self->offsets == NULL)
107 return (const char *)PyErr_NoMemory();
108 return (const char *)PyErr_NoMemory();
108 inline_scan(self, self->offsets);
109 inline_scan(self, self->offsets);
109 }
110 }
110 return self->offsets[pos];
111 return self->offsets[pos];
111 }
112 }
112
113
113 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
114 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
114 }
115 }
115
116
116 static inline int index_get_parents(indexObject *self, Py_ssize_t rev,
117 static inline int index_get_parents(indexObject *self, Py_ssize_t rev,
117 int *ps, int maxrev)
118 int *ps, int maxrev)
118 {
119 {
119 if (rev >= self->length - 1) {
120 if (rev >= self->length - 1) {
120 PyObject *tuple = PyList_GET_ITEM(self->added,
121 PyObject *tuple = PyList_GET_ITEM(self->added,
121 rev - self->length + 1);
122 rev - self->length + 1);
122 ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5));
123 ps[0] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 5));
123 ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6));
124 ps[1] = (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 6));
124 } else {
125 } else {
125 const char *data = index_deref(self, rev);
126 const char *data = index_deref(self, rev);
126 ps[0] = getbe32(data + 24);
127 ps[0] = getbe32(data + 24);
127 ps[1] = getbe32(data + 28);
128 ps[1] = getbe32(data + 28);
128 }
129 }
129 /* If index file is corrupted, ps[] may point to invalid revisions. So
130 /* If index file is corrupted, ps[] may point to invalid revisions. So
130 * there is a risk of buffer overflow to trust them unconditionally. */
131 * there is a risk of buffer overflow to trust them unconditionally. */
131 if (ps[0] > maxrev || ps[1] > maxrev) {
132 if (ps[0] > maxrev || ps[1] > maxrev) {
132 PyErr_SetString(PyExc_ValueError, "parent out of range");
133 PyErr_SetString(PyExc_ValueError, "parent out of range");
133 return -1;
134 return -1;
134 }
135 }
135 return 0;
136 return 0;
136 }
137 }
137
138
138
139
139 /*
140 /*
140 * RevlogNG format (all in big endian, data may be inlined):
141 * RevlogNG format (all in big endian, data may be inlined):
141 * 6 bytes: offset
142 * 6 bytes: offset
142 * 2 bytes: flags
143 * 2 bytes: flags
143 * 4 bytes: compressed length
144 * 4 bytes: compressed length
144 * 4 bytes: uncompressed length
145 * 4 bytes: uncompressed length
145 * 4 bytes: base revision
146 * 4 bytes: base revision
146 * 4 bytes: link revision
147 * 4 bytes: link revision
147 * 4 bytes: parent 1 revision
148 * 4 bytes: parent 1 revision
148 * 4 bytes: parent 2 revision
149 * 4 bytes: parent 2 revision
149 * 32 bytes: nodeid (only 20 bytes used)
150 * 32 bytes: nodeid (only 20 bytes used)
150 */
151 */
151 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
152 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
152 {
153 {
153 uint64_t offset_flags;
154 uint64_t offset_flags;
154 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
155 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
155 const char *c_node_id;
156 const char *c_node_id;
156 const char *data;
157 const char *data;
157 Py_ssize_t length = index_length(self);
158 Py_ssize_t length = index_length(self);
158 PyObject *entry;
159 PyObject *entry;
159
160
160 if (pos < 0)
161 if (pos < 0)
161 pos += length;
162 pos += length;
162
163
163 if (pos < 0 || pos >= length) {
164 if (pos < 0 || pos >= length) {
164 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
165 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
165 return NULL;
166 return NULL;
166 }
167 }
167
168
168 if (pos == length - 1) {
169 if (pos == length - 1) {
169 Py_INCREF(nullentry);
170 Py_INCREF(nullentry);
170 return nullentry;
171 return nullentry;
171 }
172 }
172
173
173 if (pos >= self->length - 1) {
174 if (pos >= self->length - 1) {
174 PyObject *obj;
175 PyObject *obj;
175 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
176 obj = PyList_GET_ITEM(self->added, pos - self->length + 1);
176 Py_INCREF(obj);
177 Py_INCREF(obj);
177 return obj;
178 return obj;
178 }
179 }
179
180
180 if (self->cache) {
181 if (self->cache) {
181 if (self->cache[pos]) {
182 if (self->cache[pos]) {
182 Py_INCREF(self->cache[pos]);
183 Py_INCREF(self->cache[pos]);
183 return self->cache[pos];
184 return self->cache[pos];
184 }
185 }
185 } else {
186 } else {
186 self->cache = calloc(self->raw_length, sizeof(PyObject *));
187 self->cache = calloc(self->raw_length, sizeof(PyObject *));
187 if (self->cache == NULL)
188 if (self->cache == NULL)
188 return PyErr_NoMemory();
189 return PyErr_NoMemory();
189 }
190 }
190
191
191 data = index_deref(self, pos);
192 data = index_deref(self, pos);
192 if (data == NULL)
193 if (data == NULL)
193 return NULL;
194 return NULL;
194
195
195 offset_flags = getbe32(data + 4);
196 offset_flags = getbe32(data + 4);
196 if (pos == 0) /* mask out version number for the first entry */
197 if (pos == 0) /* mask out version number for the first entry */
197 offset_flags &= 0xFFFF;
198 offset_flags &= 0xFFFF;
198 else {
199 else {
199 uint32_t offset_high = getbe32(data);
200 uint32_t offset_high = getbe32(data);
200 offset_flags |= ((uint64_t)offset_high) << 32;
201 offset_flags |= ((uint64_t)offset_high) << 32;
201 }
202 }
202
203
203 comp_len = getbe32(data + 8);
204 comp_len = getbe32(data + 8);
204 uncomp_len = getbe32(data + 12);
205 uncomp_len = getbe32(data + 12);
205 base_rev = getbe32(data + 16);
206 base_rev = getbe32(data + 16);
206 link_rev = getbe32(data + 20);
207 link_rev = getbe32(data + 20);
207 parent_1 = getbe32(data + 24);
208 parent_1 = getbe32(data + 24);
208 parent_2 = getbe32(data + 28);
209 parent_2 = getbe32(data + 28);
209 c_node_id = data + 32;
210 c_node_id = data + 32;
210
211
211 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
212 entry = Py_BuildValue(tuple_format, offset_flags, comp_len,
212 uncomp_len, base_rev, link_rev,
213 uncomp_len, base_rev, link_rev,
213 parent_1, parent_2, c_node_id, 20);
214 parent_1, parent_2, c_node_id, 20);
214
215
215 if (entry) {
216 if (entry) {
216 PyObject_GC_UnTrack(entry);
217 PyObject_GC_UnTrack(entry);
217 Py_INCREF(entry);
218 Py_INCREF(entry);
218 }
219 }
219
220
220 self->cache[pos] = entry;
221 self->cache[pos] = entry;
221
222
222 return entry;
223 return entry;
223 }
224 }
224
225
225 /*
226 /*
226 * Return the 20-byte SHA of the node corresponding to the given rev.
227 * Return the 20-byte SHA of the node corresponding to the given rev.
227 */
228 */
228 static const char *index_node(indexObject *self, Py_ssize_t pos)
229 static const char *index_node(indexObject *self, Py_ssize_t pos)
229 {
230 {
230 Py_ssize_t length = index_length(self);
231 Py_ssize_t length = index_length(self);
231 const char *data;
232 const char *data;
232
233
233 if (pos == length - 1 || pos == INT_MAX)
234 if (pos == length - 1 || pos == INT_MAX)
234 return nullid;
235 return nullid;
235
236
236 if (pos >= length)
237 if (pos >= length)
237 return NULL;
238 return NULL;
238
239
239 if (pos >= self->length - 1) {
240 if (pos >= self->length - 1) {
240 PyObject *tuple, *str;
241 PyObject *tuple, *str;
241 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
242 tuple = PyList_GET_ITEM(self->added, pos - self->length + 1);
242 str = PyTuple_GetItem(tuple, 7);
243 str = PyTuple_GetItem(tuple, 7);
243 return str ? PyBytes_AS_STRING(str) : NULL;
244 return str ? PyBytes_AS_STRING(str) : NULL;
244 }
245 }
245
246
246 data = index_deref(self, pos);
247 data = index_deref(self, pos);
247 return data ? data + 32 : NULL;
248 return data ? data + 32 : NULL;
248 }
249 }
249
250
250 static int nt_insert(indexObject *self, const char *node, int rev);
251 static int nt_insert(indexObject *self, const char *node, int rev);
251
252
252 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
253 static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen)
253 {
254 {
254 if (PyBytes_AsStringAndSize(obj, node, nodelen) == -1)
255 if (PyBytes_AsStringAndSize(obj, node, nodelen) == -1)
255 return -1;
256 return -1;
256 if (*nodelen == 20)
257 if (*nodelen == 20)
257 return 0;
258 return 0;
258 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
259 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
259 return -1;
260 return -1;
260 }
261 }
261
262
262 static PyObject *index_insert(indexObject *self, PyObject *args)
263 static PyObject *index_insert(indexObject *self, PyObject *args)
263 {
264 {
264 PyObject *obj;
265 PyObject *obj;
265 char *node;
266 char *node;
266 int index;
267 int index;
267 Py_ssize_t len, nodelen;
268 Py_ssize_t len, nodelen;
268
269
269 if (!PyArg_ParseTuple(args, "iO", &index, &obj))
270 if (!PyArg_ParseTuple(args, "iO", &index, &obj))
270 return NULL;
271 return NULL;
271
272
272 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
273 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
273 PyErr_SetString(PyExc_TypeError, "8-tuple required");
274 PyErr_SetString(PyExc_TypeError, "8-tuple required");
274 return NULL;
275 return NULL;
275 }
276 }
276
277
277 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
278 if (node_check(PyTuple_GET_ITEM(obj, 7), &node, &nodelen) == -1)
278 return NULL;
279 return NULL;
279
280
280 len = index_length(self);
281 len = index_length(self);
281
282
282 if (index < 0)
283 if (index < 0)
283 index += len;
284 index += len;
284
285
285 if (index != len - 1) {
286 if (index != len - 1) {
286 PyErr_SetString(PyExc_IndexError,
287 PyErr_SetString(PyExc_IndexError,
287 "insert only supported at index -1");
288 "insert only supported at index -1");
288 return NULL;
289 return NULL;
289 }
290 }
290
291
291 if (self->added == NULL) {
292 if (self->added == NULL) {
292 self->added = PyList_New(0);
293 self->added = PyList_New(0);
293 if (self->added == NULL)
294 if (self->added == NULL)
294 return NULL;
295 return NULL;
295 }
296 }
296
297
297 if (PyList_Append(self->added, obj) == -1)
298 if (PyList_Append(self->added, obj) == -1)
298 return NULL;
299 return NULL;
299
300
300 if (self->nt)
301 if (self->nt)
301 nt_insert(self, node, index);
302 nt_insert(self, node, index);
302
303
303 Py_CLEAR(self->headrevs);
304 Py_CLEAR(self->headrevs);
304 Py_RETURN_NONE;
305 Py_RETURN_NONE;
305 }
306 }
306
307
307 static void _index_clearcaches(indexObject *self)
308 static void _index_clearcaches(indexObject *self)
308 {
309 {
309 if (self->cache) {
310 if (self->cache) {
310 Py_ssize_t i;
311 Py_ssize_t i;
311
312
312 for (i = 0; i < self->raw_length; i++)
313 for (i = 0; i < self->raw_length; i++)
313 Py_CLEAR(self->cache[i]);
314 Py_CLEAR(self->cache[i]);
314 free(self->cache);
315 free(self->cache);
315 self->cache = NULL;
316 self->cache = NULL;
316 }
317 }
317 if (self->offsets) {
318 if (self->offsets) {
318 PyMem_Free(self->offsets);
319 PyMem_Free(self->offsets);
319 self->offsets = NULL;
320 self->offsets = NULL;
320 }
321 }
321 if (self->nt) {
322 if (self->nt) {
322 free(self->nt);
323 free(self->nt);
323 self->nt = NULL;
324 self->nt = NULL;
324 }
325 }
325 Py_CLEAR(self->headrevs);
326 Py_CLEAR(self->headrevs);
326 }
327 }
327
328
328 static PyObject *index_clearcaches(indexObject *self)
329 static PyObject *index_clearcaches(indexObject *self)
329 {
330 {
330 _index_clearcaches(self);
331 _index_clearcaches(self);
331 self->ntlength = self->ntcapacity = 0;
332 self->ntlength = self->ntcapacity = 0;
332 self->ntdepth = self->ntsplits = 0;
333 self->ntdepth = self->ntsplits = 0;
333 self->ntrev = -1;
334 self->ntrev = -1;
334 self->ntlookups = self->ntmisses = 0;
335 self->ntlookups = self->ntmisses = 0;
335 Py_RETURN_NONE;
336 Py_RETURN_NONE;
336 }
337 }
337
338
338 static PyObject *index_stats(indexObject *self)
339 static PyObject *index_stats(indexObject *self)
339 {
340 {
340 PyObject *obj = PyDict_New();
341 PyObject *obj = PyDict_New();
341 PyObject *t = NULL;
342 PyObject *t = NULL;
342
343
343 if (obj == NULL)
344 if (obj == NULL)
344 return NULL;
345 return NULL;
345
346
346 #define istat(__n, __d) \
347 #define istat(__n, __d) \
347 do { \
348 do { \
348 t = PyInt_FromSsize_t(self->__n); \
349 t = PyInt_FromSsize_t(self->__n); \
349 if (!t) \
350 if (!t) \
350 goto bail; \
351 goto bail; \
351 if (PyDict_SetItemString(obj, __d, t) == -1) \
352 if (PyDict_SetItemString(obj, __d, t) == -1) \
352 goto bail; \
353 goto bail; \
353 Py_DECREF(t); \
354 Py_DECREF(t); \
354 } while (0)
355 } while (0)
355
356
356 if (self->added) {
357 if (self->added) {
357 Py_ssize_t len = PyList_GET_SIZE(self->added);
358 Py_ssize_t len = PyList_GET_SIZE(self->added);
358 t = PyInt_FromSsize_t(len);
359 t = PyInt_FromSsize_t(len);
359 if (!t)
360 if (!t)
360 goto bail;
361 goto bail;
361 if (PyDict_SetItemString(obj, "index entries added", t) == -1)
362 if (PyDict_SetItemString(obj, "index entries added", t) == -1)
362 goto bail;
363 goto bail;
363 Py_DECREF(t);
364 Py_DECREF(t);
364 }
365 }
365
366
366 if (self->raw_length != self->length - 1)
367 if (self->raw_length != self->length - 1)
367 istat(raw_length, "revs on disk");
368 istat(raw_length, "revs on disk");
368 istat(length, "revs in memory");
369 istat(length, "revs in memory");
369 istat(ntcapacity, "node trie capacity");
370 istat(ntcapacity, "node trie capacity");
370 istat(ntdepth, "node trie depth");
371 istat(ntdepth, "node trie depth");
371 istat(ntlength, "node trie count");
372 istat(ntlength, "node trie count");
372 istat(ntlookups, "node trie lookups");
373 istat(ntlookups, "node trie lookups");
373 istat(ntmisses, "node trie misses");
374 istat(ntmisses, "node trie misses");
374 istat(ntrev, "node trie last rev scanned");
375 istat(ntrev, "node trie last rev scanned");
375 istat(ntsplits, "node trie splits");
376 istat(ntsplits, "node trie splits");
376
377
377 #undef istat
378 #undef istat
378
379
379 return obj;
380 return obj;
380
381
381 bail:
382 bail:
382 Py_XDECREF(obj);
383 Py_XDECREF(obj);
383 Py_XDECREF(t);
384 Py_XDECREF(t);
384 return NULL;
385 return NULL;
385 }
386 }
386
387
387 /*
388 /*
388 * When we cache a list, we want to be sure the caller can't mutate
389 * When we cache a list, we want to be sure the caller can't mutate
389 * the cached copy.
390 * the cached copy.
390 */
391 */
391 static PyObject *list_copy(PyObject *list)
392 static PyObject *list_copy(PyObject *list)
392 {
393 {
393 Py_ssize_t len = PyList_GET_SIZE(list);
394 Py_ssize_t len = PyList_GET_SIZE(list);
394 PyObject *newlist = PyList_New(len);
395 PyObject *newlist = PyList_New(len);
395 Py_ssize_t i;
396 Py_ssize_t i;
396
397
397 if (newlist == NULL)
398 if (newlist == NULL)
398 return NULL;
399 return NULL;
399
400
400 for (i = 0; i < len; i++) {
401 for (i = 0; i < len; i++) {
401 PyObject *obj = PyList_GET_ITEM(list, i);
402 PyObject *obj = PyList_GET_ITEM(list, i);
402 Py_INCREF(obj);
403 Py_INCREF(obj);
403 PyList_SET_ITEM(newlist, i, obj);
404 PyList_SET_ITEM(newlist, i, obj);
404 }
405 }
405
406
406 return newlist;
407 return newlist;
407 }
408 }
408
409
409 static int check_filter(PyObject *filter, Py_ssize_t arg) {
410 static int check_filter(PyObject *filter, Py_ssize_t arg) {
410 if (filter) {
411 if (filter) {
411 PyObject *arglist, *result;
412 PyObject *arglist, *result;
412 int isfiltered;
413 int isfiltered;
413
414
414 arglist = Py_BuildValue("(n)", arg);
415 arglist = Py_BuildValue("(n)", arg);
415 if (!arglist) {
416 if (!arglist) {
416 return -1;
417 return -1;
417 }
418 }
418
419
419 result = PyEval_CallObject(filter, arglist);
420 result = PyEval_CallObject(filter, arglist);
420 Py_DECREF(arglist);
421 Py_DECREF(arglist);
421 if (!result) {
422 if (!result) {
422 return -1;
423 return -1;
423 }
424 }
424
425
425 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
426 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
426 * same as this function, so we can just return it directly.*/
427 * same as this function, so we can just return it directly.*/
427 isfiltered = PyObject_IsTrue(result);
428 isfiltered = PyObject_IsTrue(result);
428 Py_DECREF(result);
429 Py_DECREF(result);
429 return isfiltered;
430 return isfiltered;
430 } else {
431 } else {
431 return 0;
432 return 0;
432 }
433 }
433 }
434 }
434
435
435 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
436 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
436 Py_ssize_t marker, char *phases)
437 Py_ssize_t marker, char *phases)
437 {
438 {
438 PyObject *iter = NULL;
439 PyObject *iter = NULL;
439 PyObject *iter_item = NULL;
440 PyObject *iter_item = NULL;
440 Py_ssize_t min_idx = index_length(self) + 1;
441 Py_ssize_t min_idx = index_length(self) + 1;
441 long iter_item_long;
442 long iter_item_long;
442
443
443 if (PyList_GET_SIZE(list) != 0) {
444 if (PyList_GET_SIZE(list) != 0) {
444 iter = PyObject_GetIter(list);
445 iter = PyObject_GetIter(list);
445 if (iter == NULL)
446 if (iter == NULL)
446 return -2;
447 return -2;
447 while ((iter_item = PyIter_Next(iter)))
448 while ((iter_item = PyIter_Next(iter)))
448 {
449 {
449 iter_item_long = PyInt_AS_LONG(iter_item);
450 iter_item_long = PyInt_AS_LONG(iter_item);
450 Py_DECREF(iter_item);
451 Py_DECREF(iter_item);
451 if (iter_item_long < min_idx)
452 if (iter_item_long < min_idx)
452 min_idx = iter_item_long;
453 min_idx = iter_item_long;
453 phases[iter_item_long] = marker;
454 phases[iter_item_long] = marker;
454 }
455 }
455 Py_DECREF(iter);
456 Py_DECREF(iter);
456 }
457 }
457
458
458 return min_idx;
459 return min_idx;
459 }
460 }
460
461
461 static inline void set_phase_from_parents(char *phases, int parent_1,
462 static inline void set_phase_from_parents(char *phases, int parent_1,
462 int parent_2, Py_ssize_t i)
463 int parent_2, Py_ssize_t i)
463 {
464 {
464 if (parent_1 >= 0 && phases[parent_1] > phases[i])
465 if (parent_1 >= 0 && phases[parent_1] > phases[i])
465 phases[i] = phases[parent_1];
466 phases[i] = phases[parent_1];
466 if (parent_2 >= 0 && phases[parent_2] > phases[i])
467 if (parent_2 >= 0 && phases[parent_2] > phases[i])
467 phases[i] = phases[parent_2];
468 phases[i] = phases[parent_2];
468 }
469 }
469
470
470 static PyObject *reachableroots2(indexObject *self, PyObject *args)
471 static PyObject *reachableroots2(indexObject *self, PyObject *args)
471 {
472 {
472
473
473 /* Input */
474 /* Input */
474 long minroot;
475 long minroot;
475 PyObject *includepatharg = NULL;
476 PyObject *includepatharg = NULL;
476 int includepath = 0;
477 int includepath = 0;
477 /* heads and roots are lists */
478 /* heads and roots are lists */
478 PyObject *heads = NULL;
479 PyObject *heads = NULL;
479 PyObject *roots = NULL;
480 PyObject *roots = NULL;
480 PyObject *reachable = NULL;
481 PyObject *reachable = NULL;
481
482
482 PyObject *val;
483 PyObject *val;
483 Py_ssize_t len = index_length(self) - 1;
484 Py_ssize_t len = index_length(self) - 1;
484 long revnum;
485 long revnum;
485 Py_ssize_t k;
486 Py_ssize_t k;
486 Py_ssize_t i;
487 Py_ssize_t i;
487 Py_ssize_t l;
488 Py_ssize_t l;
488 int r;
489 int r;
489 int parents[2];
490 int parents[2];
490
491
491 /* Internal data structure:
492 /* Internal data structure:
492 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit
493 * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit
493 * revstates: array of length len+1 (all revs + nullrev) */
494 * revstates: array of length len+1 (all revs + nullrev) */
494 int *tovisit = NULL;
495 int *tovisit = NULL;
495 long lentovisit = 0;
496 long lentovisit = 0;
496 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
497 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
497 char *revstates = NULL;
498 char *revstates = NULL;
498
499
499 /* Get arguments */
500 /* Get arguments */
500 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
501 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
501 &PyList_Type, &roots,
502 &PyList_Type, &roots,
502 &PyBool_Type, &includepatharg))
503 &PyBool_Type, &includepatharg))
503 goto bail;
504 goto bail;
504
505
505 if (includepatharg == Py_True)
506 if (includepatharg == Py_True)
506 includepath = 1;
507 includepath = 1;
507
508
508 /* Initialize return set */
509 /* Initialize return set */
509 reachable = PyList_New(0);
510 reachable = PyList_New(0);
510 if (reachable == NULL)
511 if (reachable == NULL)
511 goto bail;
512 goto bail;
512
513
513 /* Initialize internal datastructures */
514 /* Initialize internal datastructures */
514 tovisit = (int *)malloc((len + 1) * sizeof(int));
515 tovisit = (int *)malloc((len + 1) * sizeof(int));
515 if (tovisit == NULL) {
516 if (tovisit == NULL) {
516 PyErr_NoMemory();
517 PyErr_NoMemory();
517 goto bail;
518 goto bail;
518 }
519 }
519
520
520 revstates = (char *)calloc(len + 1, 1);
521 revstates = (char *)calloc(len + 1, 1);
521 if (revstates == NULL) {
522 if (revstates == NULL) {
522 PyErr_NoMemory();
523 PyErr_NoMemory();
523 goto bail;
524 goto bail;
524 }
525 }
525
526
526 l = PyList_GET_SIZE(roots);
527 l = PyList_GET_SIZE(roots);
527 for (i = 0; i < l; i++) {
528 for (i = 0; i < l; i++) {
528 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
529 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
529 if (revnum == -1 && PyErr_Occurred())
530 if (revnum == -1 && PyErr_Occurred())
530 goto bail;
531 goto bail;
531 /* If root is out of range, e.g. wdir(), it must be unreachable
532 /* If root is out of range, e.g. wdir(), it must be unreachable
532 * from heads. So we can just ignore it. */
533 * from heads. So we can just ignore it. */
533 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
534 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
534 continue;
535 continue;
535 revstates[revnum + 1] |= RS_ROOT;
536 revstates[revnum + 1] |= RS_ROOT;
536 }
537 }
537
538
538 /* Populate tovisit with all the heads */
539 /* Populate tovisit with all the heads */
539 l = PyList_GET_SIZE(heads);
540 l = PyList_GET_SIZE(heads);
540 for (i = 0; i < l; i++) {
541 for (i = 0; i < l; i++) {
541 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
542 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
542 if (revnum == -1 && PyErr_Occurred())
543 if (revnum == -1 && PyErr_Occurred())
543 goto bail;
544 goto bail;
544 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
545 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
545 PyErr_SetString(PyExc_IndexError, "head out of range");
546 PyErr_SetString(PyExc_IndexError, "head out of range");
546 goto bail;
547 goto bail;
547 }
548 }
548 if (!(revstates[revnum + 1] & RS_SEEN)) {
549 if (!(revstates[revnum + 1] & RS_SEEN)) {
549 tovisit[lentovisit++] = (int)revnum;
550 tovisit[lentovisit++] = (int)revnum;
550 revstates[revnum + 1] |= RS_SEEN;
551 revstates[revnum + 1] |= RS_SEEN;
551 }
552 }
552 }
553 }
553
554
554 /* Visit the tovisit list and find the reachable roots */
555 /* Visit the tovisit list and find the reachable roots */
555 k = 0;
556 k = 0;
556 while (k < lentovisit) {
557 while (k < lentovisit) {
557 /* Add the node to reachable if it is a root*/
558 /* Add the node to reachable if it is a root*/
558 revnum = tovisit[k++];
559 revnum = tovisit[k++];
559 if (revstates[revnum + 1] & RS_ROOT) {
560 if (revstates[revnum + 1] & RS_ROOT) {
560 revstates[revnum + 1] |= RS_REACHABLE;
561 revstates[revnum + 1] |= RS_REACHABLE;
561 val = PyInt_FromLong(revnum);
562 val = PyInt_FromLong(revnum);
562 if (val == NULL)
563 if (val == NULL)
563 goto bail;
564 goto bail;
564 r = PyList_Append(reachable, val);
565 r = PyList_Append(reachable, val);
565 Py_DECREF(val);
566 Py_DECREF(val);
566 if (r < 0)
567 if (r < 0)
567 goto bail;
568 goto bail;
568 if (includepath == 0)
569 if (includepath == 0)
569 continue;
570 continue;
570 }
571 }
571
572
572 /* Add its parents to the list of nodes to visit */
573 /* Add its parents to the list of nodes to visit */
573 if (revnum == -1)
574 if (revnum == -1)
574 continue;
575 continue;
575 r = index_get_parents(self, revnum, parents, (int)len - 1);
576 r = index_get_parents(self, revnum, parents, (int)len - 1);
576 if (r < 0)
577 if (r < 0)
577 goto bail;
578 goto bail;
578 for (i = 0; i < 2; i++) {
579 for (i = 0; i < 2; i++) {
579 if (!(revstates[parents[i] + 1] & RS_SEEN)
580 if (!(revstates[parents[i] + 1] & RS_SEEN)
580 && parents[i] >= minroot) {
581 && parents[i] >= minroot) {
581 tovisit[lentovisit++] = parents[i];
582 tovisit[lentovisit++] = parents[i];
582 revstates[parents[i] + 1] |= RS_SEEN;
583 revstates[parents[i] + 1] |= RS_SEEN;
583 }
584 }
584 }
585 }
585 }
586 }
586
587
587 /* Find all the nodes in between the roots we found and the heads
588 /* Find all the nodes in between the roots we found and the heads
588 * and add them to the reachable set */
589 * and add them to the reachable set */
589 if (includepath == 1) {
590 if (includepath == 1) {
590 long minidx = minroot;
591 long minidx = minroot;
591 if (minidx < 0)
592 if (minidx < 0)
592 minidx = 0;
593 minidx = 0;
593 for (i = minidx; i < len; i++) {
594 for (i = minidx; i < len; i++) {
594 if (!(revstates[i + 1] & RS_SEEN))
595 if (!(revstates[i + 1] & RS_SEEN))
595 continue;
596 continue;
596 r = index_get_parents(self, i, parents, (int)len - 1);
597 r = index_get_parents(self, i, parents, (int)len - 1);
597 /* Corrupted index file, error is set from
598 /* Corrupted index file, error is set from
598 * index_get_parents */
599 * index_get_parents */
599 if (r < 0)
600 if (r < 0)
600 goto bail;
601 goto bail;
601 if (((revstates[parents[0] + 1] |
602 if (((revstates[parents[0] + 1] |
602 revstates[parents[1] + 1]) & RS_REACHABLE)
603 revstates[parents[1] + 1]) & RS_REACHABLE)
603 && !(revstates[i + 1] & RS_REACHABLE)) {
604 && !(revstates[i + 1] & RS_REACHABLE)) {
604 revstates[i + 1] |= RS_REACHABLE;
605 revstates[i + 1] |= RS_REACHABLE;
605 val = PyInt_FromLong(i);
606 val = PyInt_FromLong(i);
606 if (val == NULL)
607 if (val == NULL)
607 goto bail;
608 goto bail;
608 r = PyList_Append(reachable, val);
609 r = PyList_Append(reachable, val);
609 Py_DECREF(val);
610 Py_DECREF(val);
610 if (r < 0)
611 if (r < 0)
611 goto bail;
612 goto bail;
612 }
613 }
613 }
614 }
614 }
615 }
615
616
616 free(revstates);
617 free(revstates);
617 free(tovisit);
618 free(tovisit);
618 return reachable;
619 return reachable;
619 bail:
620 bail:
620 Py_XDECREF(reachable);
621 Py_XDECREF(reachable);
621 free(revstates);
622 free(revstates);
622 free(tovisit);
623 free(tovisit);
623 return NULL;
624 return NULL;
624 }
625 }
625
626
626 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
627 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
627 {
628 {
628 PyObject *roots = Py_None;
629 PyObject *roots = Py_None;
629 PyObject *ret = NULL;
630 PyObject *ret = NULL;
630 PyObject *phaseslist = NULL;
631 PyObject *phaseslist = NULL;
631 PyObject *phaseroots = NULL;
632 PyObject *phaseroots = NULL;
632 PyObject *phaseset = NULL;
633 PyObject *phaseset = NULL;
633 PyObject *phasessetlist = NULL;
634 PyObject *phasessetlist = NULL;
634 PyObject *rev = NULL;
635 PyObject *rev = NULL;
635 Py_ssize_t len = index_length(self) - 1;
636 Py_ssize_t len = index_length(self) - 1;
636 Py_ssize_t numphase = 0;
637 Py_ssize_t numphase = 0;
637 Py_ssize_t minrevallphases = 0;
638 Py_ssize_t minrevallphases = 0;
638 Py_ssize_t minrevphase = 0;
639 Py_ssize_t minrevphase = 0;
639 Py_ssize_t i = 0;
640 Py_ssize_t i = 0;
640 char *phases = NULL;
641 char *phases = NULL;
641 long phase;
642 long phase;
642
643
643 if (!PyArg_ParseTuple(args, "O", &roots))
644 if (!PyArg_ParseTuple(args, "O", &roots))
644 goto done;
645 goto done;
645 if (roots == NULL || !PyList_Check(roots))
646 if (roots == NULL || !PyList_Check(roots))
646 goto done;
647 goto done;
647
648
648 phases = calloc(len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
649 phases = calloc(len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
649 if (phases == NULL) {
650 if (phases == NULL) {
650 PyErr_NoMemory();
651 PyErr_NoMemory();
651 goto done;
652 goto done;
652 }
653 }
653 /* Put the phase information of all the roots in phases */
654 /* Put the phase information of all the roots in phases */
654 numphase = PyList_GET_SIZE(roots)+1;
655 numphase = PyList_GET_SIZE(roots)+1;
655 minrevallphases = len + 1;
656 minrevallphases = len + 1;
656 phasessetlist = PyList_New(numphase);
657 phasessetlist = PyList_New(numphase);
657 if (phasessetlist == NULL)
658 if (phasessetlist == NULL)
658 goto done;
659 goto done;
659
660
660 PyList_SET_ITEM(phasessetlist, 0, Py_None);
661 PyList_SET_ITEM(phasessetlist, 0, Py_None);
661 Py_INCREF(Py_None);
662 Py_INCREF(Py_None);
662
663
663 for (i = 0; i < numphase-1; i++) {
664 for (i = 0; i < numphase-1; i++) {
664 phaseroots = PyList_GET_ITEM(roots, i);
665 phaseroots = PyList_GET_ITEM(roots, i);
665 phaseset = PySet_New(NULL);
666 phaseset = PySet_New(NULL);
666 if (phaseset == NULL)
667 if (phaseset == NULL)
667 goto release;
668 goto release;
668 PyList_SET_ITEM(phasessetlist, i+1, phaseset);
669 PyList_SET_ITEM(phasessetlist, i+1, phaseset);
669 if (!PyList_Check(phaseroots))
670 if (!PyList_Check(phaseroots))
670 goto release;
671 goto release;
671 minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
672 minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
672 if (minrevphase == -2) /* Error from add_roots_get_min */
673 if (minrevphase == -2) /* Error from add_roots_get_min */
673 goto release;
674 goto release;
674 minrevallphases = MIN(minrevallphases, minrevphase);
675 minrevallphases = MIN(minrevallphases, minrevphase);
675 }
676 }
676 /* Propagate the phase information from the roots to the revs */
677 /* Propagate the phase information from the roots to the revs */
677 if (minrevallphases != -1) {
678 if (minrevallphases != -1) {
678 int parents[2];
679 int parents[2];
679 for (i = minrevallphases; i < len; i++) {
680 for (i = minrevallphases; i < len; i++) {
680 if (index_get_parents(self, i, parents,
681 if (index_get_parents(self, i, parents,
681 (int)len - 1) < 0)
682 (int)len - 1) < 0)
682 goto release;
683 goto release;
683 set_phase_from_parents(phases, parents[0], parents[1], i);
684 set_phase_from_parents(phases, parents[0], parents[1], i);
684 }
685 }
685 }
686 }
686 /* Transform phase list to a python list */
687 /* Transform phase list to a python list */
687 phaseslist = PyList_New(len);
688 phaseslist = PyList_New(len);
688 if (phaseslist == NULL)
689 if (phaseslist == NULL)
689 goto release;
690 goto release;
690 for (i = 0; i < len; i++) {
691 for (i = 0; i < len; i++) {
691 PyObject *phaseval;
692 PyObject *phaseval;
692
693
693 phase = phases[i];
694 phase = phases[i];
694 /* We only store the sets of phase for non public phase, the public phase
695 /* We only store the sets of phase for non public phase, the public phase
695 * is computed as a difference */
696 * is computed as a difference */
696 if (phase != 0) {
697 if (phase != 0) {
697 phaseset = PyList_GET_ITEM(phasessetlist, phase);
698 phaseset = PyList_GET_ITEM(phasessetlist, phase);
698 rev = PyInt_FromLong(i);
699 rev = PyInt_FromLong(i);
699 if (rev == NULL)
700 if (rev == NULL)
700 goto release;
701 goto release;
701 PySet_Add(phaseset, rev);
702 PySet_Add(phaseset, rev);
702 Py_XDECREF(rev);
703 Py_XDECREF(rev);
703 }
704 }
704 phaseval = PyInt_FromLong(phase);
705 phaseval = PyInt_FromLong(phase);
705 if (phaseval == NULL)
706 if (phaseval == NULL)
706 goto release;
707 goto release;
707 PyList_SET_ITEM(phaseslist, i, phaseval);
708 PyList_SET_ITEM(phaseslist, i, phaseval);
708 }
709 }
709 ret = PyTuple_Pack(2, phaseslist, phasessetlist);
710 ret = PyTuple_Pack(2, phaseslist, phasessetlist);
710
711
711 release:
712 release:
712 Py_XDECREF(phaseslist);
713 Py_XDECREF(phaseslist);
713 Py_XDECREF(phasessetlist);
714 Py_XDECREF(phasessetlist);
714 done:
715 done:
715 free(phases);
716 free(phases);
716 return ret;
717 return ret;
717 }
718 }
718
719
719 static PyObject *index_headrevs(indexObject *self, PyObject *args)
720 static PyObject *index_headrevs(indexObject *self, PyObject *args)
720 {
721 {
721 Py_ssize_t i, j, len;
722 Py_ssize_t i, j, len;
722 char *nothead = NULL;
723 char *nothead = NULL;
723 PyObject *heads = NULL;
724 PyObject *heads = NULL;
724 PyObject *filter = NULL;
725 PyObject *filter = NULL;
725 PyObject *filteredrevs = Py_None;
726 PyObject *filteredrevs = Py_None;
726
727
727 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
728 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
728 return NULL;
729 return NULL;
729 }
730 }
730
731
731 if (self->headrevs && filteredrevs == self->filteredrevs)
732 if (self->headrevs && filteredrevs == self->filteredrevs)
732 return list_copy(self->headrevs);
733 return list_copy(self->headrevs);
733
734
734 Py_DECREF(self->filteredrevs);
735 Py_DECREF(self->filteredrevs);
735 self->filteredrevs = filteredrevs;
736 self->filteredrevs = filteredrevs;
736 Py_INCREF(filteredrevs);
737 Py_INCREF(filteredrevs);
737
738
738 if (filteredrevs != Py_None) {
739 if (filteredrevs != Py_None) {
739 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
740 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
740 if (!filter) {
741 if (!filter) {
741 PyErr_SetString(PyExc_TypeError,
742 PyErr_SetString(PyExc_TypeError,
742 "filteredrevs has no attribute __contains__");
743 "filteredrevs has no attribute __contains__");
743 goto bail;
744 goto bail;
744 }
745 }
745 }
746 }
746
747
747 len = index_length(self) - 1;
748 len = index_length(self) - 1;
748 heads = PyList_New(0);
749 heads = PyList_New(0);
749 if (heads == NULL)
750 if (heads == NULL)
750 goto bail;
751 goto bail;
751 if (len == 0) {
752 if (len == 0) {
752 PyObject *nullid = PyInt_FromLong(-1);
753 PyObject *nullid = PyInt_FromLong(-1);
753 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
754 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
754 Py_XDECREF(nullid);
755 Py_XDECREF(nullid);
755 goto bail;
756 goto bail;
756 }
757 }
757 goto done;
758 goto done;
758 }
759 }
759
760
760 nothead = calloc(len, 1);
761 nothead = calloc(len, 1);
761 if (nothead == NULL) {
762 if (nothead == NULL) {
762 PyErr_NoMemory();
763 PyErr_NoMemory();
763 goto bail;
764 goto bail;
764 }
765 }
765
766
766 for (i = len - 1; i >= 0; i--) {
767 for (i = len - 1; i >= 0; i--) {
767 int isfiltered;
768 int isfiltered;
768 int parents[2];
769 int parents[2];
769
770
770 /* If nothead[i] == 1, it means we've seen an unfiltered child of this
771 /* If nothead[i] == 1, it means we've seen an unfiltered child of this
771 * node already, and therefore this node is not filtered. So we can skip
772 * node already, and therefore this node is not filtered. So we can skip
772 * the expensive check_filter step.
773 * the expensive check_filter step.
773 */
774 */
774 if (nothead[i] != 1) {
775 if (nothead[i] != 1) {
775 isfiltered = check_filter(filter, i);
776 isfiltered = check_filter(filter, i);
776 if (isfiltered == -1) {
777 if (isfiltered == -1) {
777 PyErr_SetString(PyExc_TypeError,
778 PyErr_SetString(PyExc_TypeError,
778 "unable to check filter");
779 "unable to check filter");
779 goto bail;
780 goto bail;
780 }
781 }
781
782
782 if (isfiltered) {
783 if (isfiltered) {
783 nothead[i] = 1;
784 nothead[i] = 1;
784 continue;
785 continue;
785 }
786 }
786 }
787 }
787
788
788 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
789 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
789 goto bail;
790 goto bail;
790 for (j = 0; j < 2; j++) {
791 for (j = 0; j < 2; j++) {
791 if (parents[j] >= 0)
792 if (parents[j] >= 0)
792 nothead[parents[j]] = 1;
793 nothead[parents[j]] = 1;
793 }
794 }
794 }
795 }
795
796
796 for (i = 0; i < len; i++) {
797 for (i = 0; i < len; i++) {
797 PyObject *head;
798 PyObject *head;
798
799
799 if (nothead[i])
800 if (nothead[i])
800 continue;
801 continue;
801 head = PyInt_FromSsize_t(i);
802 head = PyInt_FromSsize_t(i);
802 if (head == NULL || PyList_Append(heads, head) == -1) {
803 if (head == NULL || PyList_Append(heads, head) == -1) {
803 Py_XDECREF(head);
804 Py_XDECREF(head);
804 goto bail;
805 goto bail;
805 }
806 }
806 }
807 }
807
808
808 done:
809 done:
809 self->headrevs = heads;
810 self->headrevs = heads;
810 Py_XDECREF(filter);
811 Py_XDECREF(filter);
811 free(nothead);
812 free(nothead);
812 return list_copy(self->headrevs);
813 return list_copy(self->headrevs);
813 bail:
814 bail:
814 Py_XDECREF(filter);
815 Py_XDECREF(filter);
815 Py_XDECREF(heads);
816 Py_XDECREF(heads);
816 free(nothead);
817 free(nothead);
817 return NULL;
818 return NULL;
818 }
819 }
819
820
820 /**
821 /**
821 * Obtain the base revision index entry.
822 * Obtain the base revision index entry.
822 *
823 *
823 * Callers must ensure that rev >= 0 or illegal memory access may occur.
824 * Callers must ensure that rev >= 0 or illegal memory access may occur.
824 */
825 */
825 static inline int index_baserev(indexObject *self, int rev)
826 static inline int index_baserev(indexObject *self, int rev)
826 {
827 {
827 const char *data;
828 const char *data;
828
829
829 if (rev >= self->length - 1) {
830 if (rev >= self->length - 1) {
830 PyObject *tuple = PyList_GET_ITEM(self->added,
831 PyObject *tuple = PyList_GET_ITEM(self->added,
831 rev - self->length + 1);
832 rev - self->length + 1);
832 return (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 3));
833 return (int)PyInt_AS_LONG(PyTuple_GET_ITEM(tuple, 3));
833 }
834 }
834 else {
835 else {
835 data = index_deref(self, rev);
836 data = index_deref(self, rev);
836 if (data == NULL) {
837 if (data == NULL) {
837 return -2;
838 return -2;
838 }
839 }
839
840
840 return getbe32(data + 16);
841 return getbe32(data + 16);
841 }
842 }
842 }
843 }
843
844
844 static PyObject *index_deltachain(indexObject *self, PyObject *args)
845 static PyObject *index_deltachain(indexObject *self, PyObject *args)
845 {
846 {
846 int rev, generaldelta;
847 int rev, generaldelta;
847 PyObject *stoparg;
848 PyObject *stoparg;
848 int stoprev, iterrev, baserev = -1;
849 int stoprev, iterrev, baserev = -1;
849 int stopped;
850 int stopped;
850 PyObject *chain = NULL, *result = NULL;
851 PyObject *chain = NULL, *result = NULL;
851 const Py_ssize_t length = index_length(self);
852 const Py_ssize_t length = index_length(self);
852
853
853 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
854 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
854 return NULL;
855 return NULL;
855 }
856 }
856
857
857 if (PyInt_Check(stoparg)) {
858 if (PyInt_Check(stoparg)) {
858 stoprev = (int)PyInt_AsLong(stoparg);
859 stoprev = (int)PyInt_AsLong(stoparg);
859 if (stoprev == -1 && PyErr_Occurred()) {
860 if (stoprev == -1 && PyErr_Occurred()) {
860 return NULL;
861 return NULL;
861 }
862 }
862 }
863 }
863 else if (stoparg == Py_None) {
864 else if (stoparg == Py_None) {
864 stoprev = -2;
865 stoprev = -2;
865 }
866 }
866 else {
867 else {
867 PyErr_SetString(PyExc_ValueError,
868 PyErr_SetString(PyExc_ValueError,
868 "stoprev must be integer or None");
869 "stoprev must be integer or None");
869 return NULL;
870 return NULL;
870 }
871 }
871
872
872 if (rev < 0 || rev >= length - 1) {
873 if (rev < 0 || rev >= length - 1) {
873 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
874 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
874 return NULL;
875 return NULL;
875 }
876 }
876
877
877 chain = PyList_New(0);
878 chain = PyList_New(0);
878 if (chain == NULL) {
879 if (chain == NULL) {
879 return NULL;
880 return NULL;
880 }
881 }
881
882
882 baserev = index_baserev(self, rev);
883 baserev = index_baserev(self, rev);
883
884
884 /* This should never happen. */
885 /* This should never happen. */
885 if (baserev <= -2) {
886 if (baserev <= -2) {
886 /* Error should be set by index_deref() */
887 /* Error should be set by index_deref() */
887 assert(PyErr_Occurred());
888 assert(PyErr_Occurred());
888 goto bail;
889 goto bail;
889 }
890 }
890
891
891 iterrev = rev;
892 iterrev = rev;
892
893
893 while (iterrev != baserev && iterrev != stoprev) {
894 while (iterrev != baserev && iterrev != stoprev) {
894 PyObject *value = PyInt_FromLong(iterrev);
895 PyObject *value = PyInt_FromLong(iterrev);
895 if (value == NULL) {
896 if (value == NULL) {
896 goto bail;
897 goto bail;
897 }
898 }
898 if (PyList_Append(chain, value)) {
899 if (PyList_Append(chain, value)) {
899 Py_DECREF(value);
900 Py_DECREF(value);
900 goto bail;
901 goto bail;
901 }
902 }
902 Py_DECREF(value);
903 Py_DECREF(value);
903
904
904 if (generaldelta) {
905 if (generaldelta) {
905 iterrev = baserev;
906 iterrev = baserev;
906 }
907 }
907 else {
908 else {
908 iterrev--;
909 iterrev--;
909 }
910 }
910
911
911 if (iterrev < 0) {
912 if (iterrev < 0) {
912 break;
913 break;
913 }
914 }
914
915
915 if (iterrev >= length - 1) {
916 if (iterrev >= length - 1) {
916 PyErr_SetString(PyExc_IndexError, "revision outside index");
917 PyErr_SetString(PyExc_IndexError, "revision outside index");
917 return NULL;
918 return NULL;
918 }
919 }
919
920
920 baserev = index_baserev(self, iterrev);
921 baserev = index_baserev(self, iterrev);
921
922
922 /* This should never happen. */
923 /* This should never happen. */
923 if (baserev <= -2) {
924 if (baserev <= -2) {
924 /* Error should be set by index_deref() */
925 /* Error should be set by index_deref() */
925 assert(PyErr_Occurred());
926 assert(PyErr_Occurred());
926 goto bail;
927 goto bail;
927 }
928 }
928 }
929 }
929
930
930 if (iterrev == stoprev) {
931 if (iterrev == stoprev) {
931 stopped = 1;
932 stopped = 1;
932 }
933 }
933 else {
934 else {
934 PyObject *value = PyInt_FromLong(iterrev);
935 PyObject *value = PyInt_FromLong(iterrev);
935 if (value == NULL) {
936 if (value == NULL) {
936 goto bail;
937 goto bail;
937 }
938 }
938 if (PyList_Append(chain, value)) {
939 if (PyList_Append(chain, value)) {
939 Py_DECREF(value);
940 Py_DECREF(value);
940 goto bail;
941 goto bail;
941 }
942 }
942 Py_DECREF(value);
943 Py_DECREF(value);
943
944
944 stopped = 0;
945 stopped = 0;
945 }
946 }
946
947
947 if (PyList_Reverse(chain)) {
948 if (PyList_Reverse(chain)) {
948 goto bail;
949 goto bail;
949 }
950 }
950
951
951 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
952 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
952 Py_DECREF(chain);
953 Py_DECREF(chain);
953 return result;
954 return result;
954
955
955 bail:
956 bail:
956 Py_DECREF(chain);
957 Py_DECREF(chain);
957 return NULL;
958 return NULL;
958 }
959 }
959
960
960 static inline int nt_level(const char *node, Py_ssize_t level)
961 static inline int nt_level(const char *node, Py_ssize_t level)
961 {
962 {
962 int v = node[level>>1];
963 int v = node[level>>1];
963 if (!(level & 1))
964 if (!(level & 1))
964 v >>= 4;
965 v >>= 4;
965 return v & 0xf;
966 return v & 0xf;
966 }
967 }
967
968
968 /*
969 /*
969 * Return values:
970 * Return values:
970 *
971 *
971 * -4: match is ambiguous (multiple candidates)
972 * -4: match is ambiguous (multiple candidates)
972 * -2: not found
973 * -2: not found
973 * rest: valid rev
974 * rest: valid rev
974 */
975 */
975 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
976 static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen,
976 int hex)
977 int hex)
977 {
978 {
978 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
979 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
979 int level, maxlevel, off;
980 int level, maxlevel, off;
980
981
981 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
982 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
982 return -1;
983 return -1;
983
984
984 if (self->nt == NULL)
985 if (self->nt == NULL)
985 return -2;
986 return -2;
986
987
987 if (hex)
988 if (hex)
988 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
989 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
989 else
990 else
990 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
991 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
991
992
992 for (level = off = 0; level < maxlevel; level++) {
993 for (level = off = 0; level < maxlevel; level++) {
993 int k = getnybble(node, level);
994 int k = getnybble(node, level);
994 nodetree *n = &self->nt[off];
995 nodetree *n = &self->nt[off];
995 int v = n->children[k];
996 int v = n->children[k];
996
997
997 if (v < 0) {
998 if (v < 0) {
998 const char *n;
999 const char *n;
999 Py_ssize_t i;
1000 Py_ssize_t i;
1000
1001
1001 v = -(v + 1);
1002 v = -(v + 1);
1002 n = index_node(self, v);
1003 n = index_node(self, v);
1003 if (n == NULL)
1004 if (n == NULL)
1004 return -2;
1005 return -2;
1005 for (i = level; i < maxlevel; i++)
1006 for (i = level; i < maxlevel; i++)
1006 if (getnybble(node, i) != nt_level(n, i))
1007 if (getnybble(node, i) != nt_level(n, i))
1007 return -2;
1008 return -2;
1008 return v;
1009 return v;
1009 }
1010 }
1010 if (v == 0)
1011 if (v == 0)
1011 return -2;
1012 return -2;
1012 off = v;
1013 off = v;
1013 }
1014 }
1014 /* multiple matches against an ambiguous prefix */
1015 /* multiple matches against an ambiguous prefix */
1015 return -4;
1016 return -4;
1016 }
1017 }
1017
1018
1018 static int nt_new(indexObject *self)
1019 static int nt_new(indexObject *self)
1019 {
1020 {
1020 if (self->ntlength == self->ntcapacity) {
1021 if (self->ntlength == self->ntcapacity) {
1021 if (self->ntcapacity >= INT_MAX / (sizeof(nodetree) * 2)) {
1022 if (self->ntcapacity >= INT_MAX / (sizeof(nodetree) * 2)) {
1022 PyErr_SetString(PyExc_MemoryError,
1023 PyErr_SetString(PyExc_MemoryError,
1023 "overflow in nt_new");
1024 "overflow in nt_new");
1024 return -1;
1025 return -1;
1025 }
1026 }
1026 self->ntcapacity *= 2;
1027 self->ntcapacity *= 2;
1027 self->nt = realloc(self->nt,
1028 self->nt = realloc(self->nt,
1028 self->ntcapacity * sizeof(nodetree));
1029 self->ntcapacity * sizeof(nodetree));
1029 if (self->nt == NULL) {
1030 if (self->nt == NULL) {
1030 PyErr_SetString(PyExc_MemoryError, "out of memory");
1031 PyErr_SetString(PyExc_MemoryError, "out of memory");
1031 return -1;
1032 return -1;
1032 }
1033 }
1033 memset(&self->nt[self->ntlength], 0,
1034 memset(&self->nt[self->ntlength], 0,
1034 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
1035 sizeof(nodetree) * (self->ntcapacity - self->ntlength));
1035 }
1036 }
1036 return self->ntlength++;
1037 return self->ntlength++;
1037 }
1038 }
1038
1039
1039 static int nt_insert(indexObject *self, const char *node, int rev)
1040 static int nt_insert(indexObject *self, const char *node, int rev)
1040 {
1041 {
1041 int level = 0;
1042 int level = 0;
1042 int off = 0;
1043 int off = 0;
1043
1044
1044 while (level < 40) {
1045 while (level < 40) {
1045 int k = nt_level(node, level);
1046 int k = nt_level(node, level);
1046 nodetree *n;
1047 nodetree *n;
1047 int v;
1048 int v;
1048
1049
1049 n = &self->nt[off];
1050 n = &self->nt[off];
1050 v = n->children[k];
1051 v = n->children[k];
1051
1052
1052 if (v == 0) {
1053 if (v == 0) {
1053 n->children[k] = -rev - 1;
1054 n->children[k] = -rev - 1;
1054 return 0;
1055 return 0;
1055 }
1056 }
1056 if (v < 0) {
1057 if (v < 0) {
1057 const char *oldnode = index_node(self, -(v + 1));
1058 const char *oldnode = index_node(self, -(v + 1));
1058 int noff;
1059 int noff;
1059
1060
1060 if (!oldnode || !memcmp(oldnode, node, 20)) {
1061 if (!oldnode || !memcmp(oldnode, node, 20)) {
1061 n->children[k] = -rev - 1;
1062 n->children[k] = -rev - 1;
1062 return 0;
1063 return 0;
1063 }
1064 }
1064 noff = nt_new(self);
1065 noff = nt_new(self);
1065 if (noff == -1)
1066 if (noff == -1)
1066 return -1;
1067 return -1;
1067 /* self->nt may have been changed by realloc */
1068 /* self->nt may have been changed by realloc */
1068 self->nt[off].children[k] = noff;
1069 self->nt[off].children[k] = noff;
1069 off = noff;
1070 off = noff;
1070 n = &self->nt[off];
1071 n = &self->nt[off];
1071 n->children[nt_level(oldnode, ++level)] = v;
1072 n->children[nt_level(oldnode, ++level)] = v;
1072 if (level > self->ntdepth)
1073 if (level > self->ntdepth)
1073 self->ntdepth = level;
1074 self->ntdepth = level;
1074 self->ntsplits += 1;
1075 self->ntsplits += 1;
1075 } else {
1076 } else {
1076 level += 1;
1077 level += 1;
1077 off = v;
1078 off = v;
1078 }
1079 }
1079 }
1080 }
1080
1081
1081 return -1;
1082 return -1;
1082 }
1083 }
1083
1084
1084 static int nt_init(indexObject *self)
1085 static int nt_init(indexObject *self)
1085 {
1086 {
1086 if (self->nt == NULL) {
1087 if (self->nt == NULL) {
1087 if ((size_t)self->raw_length > INT_MAX / sizeof(nodetree)) {
1088 if ((size_t)self->raw_length > INT_MAX / sizeof(nodetree)) {
1088 PyErr_SetString(PyExc_ValueError, "overflow in nt_init");
1089 PyErr_SetString(PyExc_ValueError, "overflow in nt_init");
1089 return -1;
1090 return -1;
1090 }
1091 }
1091 self->ntcapacity = self->raw_length < 4
1092 self->ntcapacity = self->raw_length < 4
1092 ? 4 : (int)self->raw_length / 2;
1093 ? 4 : (int)self->raw_length / 2;
1093
1094
1094 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
1095 self->nt = calloc(self->ntcapacity, sizeof(nodetree));
1095 if (self->nt == NULL) {
1096 if (self->nt == NULL) {
1096 PyErr_NoMemory();
1097 PyErr_NoMemory();
1097 return -1;
1098 return -1;
1098 }
1099 }
1099 self->ntlength = 1;
1100 self->ntlength = 1;
1100 self->ntrev = (int)index_length(self) - 1;
1101 self->ntrev = (int)index_length(self) - 1;
1101 self->ntlookups = 1;
1102 self->ntlookups = 1;
1102 self->ntmisses = 0;
1103 self->ntmisses = 0;
1103 if (nt_insert(self, nullid, INT_MAX) == -1)
1104 if (nt_insert(self, nullid, INT_MAX) == -1)
1104 return -1;
1105 return -1;
1105 }
1106 }
1106 return 0;
1107 return 0;
1107 }
1108 }
1108
1109
1109 /*
1110 /*
1110 * Return values:
1111 * Return values:
1111 *
1112 *
1112 * -3: error (exception set)
1113 * -3: error (exception set)
1113 * -2: not found (no exception set)
1114 * -2: not found (no exception set)
1114 * rest: valid rev
1115 * rest: valid rev
1115 */
1116 */
1116 static int index_find_node(indexObject *self,
1117 static int index_find_node(indexObject *self,
1117 const char *node, Py_ssize_t nodelen)
1118 const char *node, Py_ssize_t nodelen)
1118 {
1119 {
1119 int rev;
1120 int rev;
1120
1121
1121 self->ntlookups++;
1122 self->ntlookups++;
1122 rev = nt_find(self, node, nodelen, 0);
1123 rev = nt_find(self, node, nodelen, 0);
1123 if (rev >= -1)
1124 if (rev >= -1)
1124 return rev;
1125 return rev;
1125
1126
1126 if (nt_init(self) == -1)
1127 if (nt_init(self) == -1)
1127 return -3;
1128 return -3;
1128
1129
1129 /*
1130 /*
1130 * For the first handful of lookups, we scan the entire index,
1131 * For the first handful of lookups, we scan the entire index,
1131 * and cache only the matching nodes. This optimizes for cases
1132 * and cache only the matching nodes. This optimizes for cases
1132 * like "hg tip", where only a few nodes are accessed.
1133 * like "hg tip", where only a few nodes are accessed.
1133 *
1134 *
1134 * After that, we cache every node we visit, using a single
1135 * After that, we cache every node we visit, using a single
1135 * scan amortized over multiple lookups. This gives the best
1136 * scan amortized over multiple lookups. This gives the best
1136 * bulk performance, e.g. for "hg log".
1137 * bulk performance, e.g. for "hg log".
1137 */
1138 */
1138 if (self->ntmisses++ < 4) {
1139 if (self->ntmisses++ < 4) {
1139 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1140 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1140 const char *n = index_node(self, rev);
1141 const char *n = index_node(self, rev);
1141 if (n == NULL)
1142 if (n == NULL)
1142 return -2;
1143 return -2;
1143 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1144 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1144 if (nt_insert(self, n, rev) == -1)
1145 if (nt_insert(self, n, rev) == -1)
1145 return -3;
1146 return -3;
1146 break;
1147 break;
1147 }
1148 }
1148 }
1149 }
1149 } else {
1150 } else {
1150 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1151 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1151 const char *n = index_node(self, rev);
1152 const char *n = index_node(self, rev);
1152 if (n == NULL) {
1153 if (n == NULL) {
1153 self->ntrev = rev + 1;
1154 self->ntrev = rev + 1;
1154 return -2;
1155 return -2;
1155 }
1156 }
1156 if (nt_insert(self, n, rev) == -1) {
1157 if (nt_insert(self, n, rev) == -1) {
1157 self->ntrev = rev + 1;
1158 self->ntrev = rev + 1;
1158 return -3;
1159 return -3;
1159 }
1160 }
1160 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1161 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1161 break;
1162 break;
1162 }
1163 }
1163 }
1164 }
1164 self->ntrev = rev;
1165 self->ntrev = rev;
1165 }
1166 }
1166
1167
1167 if (rev >= 0)
1168 if (rev >= 0)
1168 return rev;
1169 return rev;
1169 return -2;
1170 return -2;
1170 }
1171 }
1171
1172
1172 static void raise_revlog_error(void)
1173 static void raise_revlog_error(void)
1173 {
1174 {
1174 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
1175 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
1175
1176
1176 mod = PyImport_ImportModule("mercurial.error");
1177 mod = PyImport_ImportModule("mercurial.error");
1177 if (mod == NULL) {
1178 if (mod == NULL) {
1178 goto cleanup;
1179 goto cleanup;
1179 }
1180 }
1180
1181
1181 dict = PyModule_GetDict(mod);
1182 dict = PyModule_GetDict(mod);
1182 if (dict == NULL) {
1183 if (dict == NULL) {
1183 goto cleanup;
1184 goto cleanup;
1184 }
1185 }
1185 Py_INCREF(dict);
1186 Py_INCREF(dict);
1186
1187
1187 errclass = PyDict_GetItemString(dict, "RevlogError");
1188 errclass = PyDict_GetItemString(dict, "RevlogError");
1188 if (errclass == NULL) {
1189 if (errclass == NULL) {
1189 PyErr_SetString(PyExc_SystemError,
1190 PyErr_SetString(PyExc_SystemError,
1190 "could not find RevlogError");
1191 "could not find RevlogError");
1191 goto cleanup;
1192 goto cleanup;
1192 }
1193 }
1193
1194
1194 /* value of exception is ignored by callers */
1195 /* value of exception is ignored by callers */
1195 PyErr_SetString(errclass, "RevlogError");
1196 PyErr_SetString(errclass, "RevlogError");
1196
1197
1197 cleanup:
1198 cleanup:
1198 Py_XDECREF(dict);
1199 Py_XDECREF(dict);
1199 Py_XDECREF(mod);
1200 Py_XDECREF(mod);
1200 }
1201 }
1201
1202
1202 static PyObject *index_getitem(indexObject *self, PyObject *value)
1203 static PyObject *index_getitem(indexObject *self, PyObject *value)
1203 {
1204 {
1204 char *node;
1205 char *node;
1205 Py_ssize_t nodelen;
1206 Py_ssize_t nodelen;
1206 int rev;
1207 int rev;
1207
1208
1208 if (PyInt_Check(value))
1209 if (PyInt_Check(value))
1209 return index_get(self, PyInt_AS_LONG(value));
1210 return index_get(self, PyInt_AS_LONG(value));
1210
1211
1211 if (node_check(value, &node, &nodelen) == -1)
1212 if (node_check(value, &node, &nodelen) == -1)
1212 return NULL;
1213 return NULL;
1213 rev = index_find_node(self, node, nodelen);
1214 rev = index_find_node(self, node, nodelen);
1214 if (rev >= -1)
1215 if (rev >= -1)
1215 return PyInt_FromLong(rev);
1216 return PyInt_FromLong(rev);
1216 if (rev == -2)
1217 if (rev == -2)
1217 raise_revlog_error();
1218 raise_revlog_error();
1218 return NULL;
1219 return NULL;
1219 }
1220 }
1220
1221
1221 static int nt_partialmatch(indexObject *self, const char *node,
1222 static int nt_partialmatch(indexObject *self, const char *node,
1222 Py_ssize_t nodelen)
1223 Py_ssize_t nodelen)
1223 {
1224 {
1224 int rev;
1225 int rev;
1225
1226
1226 if (nt_init(self) == -1)
1227 if (nt_init(self) == -1)
1227 return -3;
1228 return -3;
1228
1229
1229 if (self->ntrev > 0) {
1230 if (self->ntrev > 0) {
1230 /* ensure that the radix tree is fully populated */
1231 /* ensure that the radix tree is fully populated */
1231 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1232 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1232 const char *n = index_node(self, rev);
1233 const char *n = index_node(self, rev);
1233 if (n == NULL)
1234 if (n == NULL)
1234 return -2;
1235 return -2;
1235 if (nt_insert(self, n, rev) == -1)
1236 if (nt_insert(self, n, rev) == -1)
1236 return -3;
1237 return -3;
1237 }
1238 }
1238 self->ntrev = rev;
1239 self->ntrev = rev;
1239 }
1240 }
1240
1241
1241 return nt_find(self, node, nodelen, 1);
1242 return nt_find(self, node, nodelen, 1);
1242 }
1243 }
1243
1244
1244 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1245 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1245 {
1246 {
1246 const char *fullnode;
1247 const char *fullnode;
1247 int nodelen;
1248 int nodelen;
1248 char *node;
1249 char *node;
1249 int rev, i;
1250 int rev, i;
1250
1251
1251 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1252 if (!PyArg_ParseTuple(args, "s#", &node, &nodelen))
1252 return NULL;
1253 return NULL;
1253
1254
1254 if (nodelen < 4) {
1255 if (nodelen < 4) {
1255 PyErr_SetString(PyExc_ValueError, "key too short");
1256 PyErr_SetString(PyExc_ValueError, "key too short");
1256 return NULL;
1257 return NULL;
1257 }
1258 }
1258
1259
1259 if (nodelen > 40) {
1260 if (nodelen > 40) {
1260 PyErr_SetString(PyExc_ValueError, "key too long");
1261 PyErr_SetString(PyExc_ValueError, "key too long");
1261 return NULL;
1262 return NULL;
1262 }
1263 }
1263
1264
1264 for (i = 0; i < nodelen; i++)
1265 for (i = 0; i < nodelen; i++)
1265 hexdigit(node, i);
1266 hexdigit(node, i);
1266 if (PyErr_Occurred()) {
1267 if (PyErr_Occurred()) {
1267 /* input contains non-hex characters */
1268 /* input contains non-hex characters */
1268 PyErr_Clear();
1269 PyErr_Clear();
1269 Py_RETURN_NONE;
1270 Py_RETURN_NONE;
1270 }
1271 }
1271
1272
1272 rev = nt_partialmatch(self, node, nodelen);
1273 rev = nt_partialmatch(self, node, nodelen);
1273
1274
1274 switch (rev) {
1275 switch (rev) {
1275 case -4:
1276 case -4:
1276 raise_revlog_error();
1277 raise_revlog_error();
1277 case -3:
1278 case -3:
1278 return NULL;
1279 return NULL;
1279 case -2:
1280 case -2:
1280 Py_RETURN_NONE;
1281 Py_RETURN_NONE;
1281 case -1:
1282 case -1:
1282 return PyBytes_FromStringAndSize(nullid, 20);
1283 return PyBytes_FromStringAndSize(nullid, 20);
1283 }
1284 }
1284
1285
1285 fullnode = index_node(self, rev);
1286 fullnode = index_node(self, rev);
1286 if (fullnode == NULL) {
1287 if (fullnode == NULL) {
1287 PyErr_Format(PyExc_IndexError,
1288 PyErr_Format(PyExc_IndexError,
1288 "could not access rev %d", rev);
1289 "could not access rev %d", rev);
1289 return NULL;
1290 return NULL;
1290 }
1291 }
1291 return PyBytes_FromStringAndSize(fullnode, 20);
1292 return PyBytes_FromStringAndSize(fullnode, 20);
1292 }
1293 }
1293
1294
1294 static PyObject *index_m_get(indexObject *self, PyObject *args)
1295 static PyObject *index_m_get(indexObject *self, PyObject *args)
1295 {
1296 {
1296 Py_ssize_t nodelen;
1297 Py_ssize_t nodelen;
1297 PyObject *val;
1298 PyObject *val;
1298 char *node;
1299 char *node;
1299 int rev;
1300 int rev;
1300
1301
1301 if (!PyArg_ParseTuple(args, "O", &val))
1302 if (!PyArg_ParseTuple(args, "O", &val))
1302 return NULL;
1303 return NULL;
1303 if (node_check(val, &node, &nodelen) == -1)
1304 if (node_check(val, &node, &nodelen) == -1)
1304 return NULL;
1305 return NULL;
1305 rev = index_find_node(self, node, nodelen);
1306 rev = index_find_node(self, node, nodelen);
1306 if (rev == -3)
1307 if (rev == -3)
1307 return NULL;
1308 return NULL;
1308 if (rev == -2)
1309 if (rev == -2)
1309 Py_RETURN_NONE;
1310 Py_RETURN_NONE;
1310 return PyInt_FromLong(rev);
1311 return PyInt_FromLong(rev);
1311 }
1312 }
1312
1313
1313 static int index_contains(indexObject *self, PyObject *value)
1314 static int index_contains(indexObject *self, PyObject *value)
1314 {
1315 {
1315 char *node;
1316 char *node;
1316 Py_ssize_t nodelen;
1317 Py_ssize_t nodelen;
1317
1318
1318 if (PyInt_Check(value)) {
1319 if (PyInt_Check(value)) {
1319 long rev = PyInt_AS_LONG(value);
1320 long rev = PyInt_AS_LONG(value);
1320 return rev >= -1 && rev < index_length(self);
1321 return rev >= -1 && rev < index_length(self);
1321 }
1322 }
1322
1323
1323 if (node_check(value, &node, &nodelen) == -1)
1324 if (node_check(value, &node, &nodelen) == -1)
1324 return -1;
1325 return -1;
1325
1326
1326 switch (index_find_node(self, node, nodelen)) {
1327 switch (index_find_node(self, node, nodelen)) {
1327 case -3:
1328 case -3:
1328 return -1;
1329 return -1;
1329 case -2:
1330 case -2:
1330 return 0;
1331 return 0;
1331 default:
1332 default:
1332 return 1;
1333 return 1;
1333 }
1334 }
1334 }
1335 }
1335
1336
1336 typedef uint64_t bitmask;
1337 typedef uint64_t bitmask;
1337
1338
1338 /*
1339 /*
1339 * Given a disjoint set of revs, return all candidates for the
1340 * Given a disjoint set of revs, return all candidates for the
1340 * greatest common ancestor. In revset notation, this is the set
1341 * greatest common ancestor. In revset notation, this is the set
1341 * "heads(::a and ::b and ...)"
1342 * "heads(::a and ::b and ...)"
1342 */
1343 */
1343 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
1344 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
1344 int revcount)
1345 int revcount)
1345 {
1346 {
1346 const bitmask allseen = (1ull << revcount) - 1;
1347 const bitmask allseen = (1ull << revcount) - 1;
1347 const bitmask poison = 1ull << revcount;
1348 const bitmask poison = 1ull << revcount;
1348 PyObject *gca = PyList_New(0);
1349 PyObject *gca = PyList_New(0);
1349 int i, v, interesting;
1350 int i, v, interesting;
1350 int maxrev = -1;
1351 int maxrev = -1;
1351 bitmask sp;
1352 bitmask sp;
1352 bitmask *seen;
1353 bitmask *seen;
1353
1354
1354 if (gca == NULL)
1355 if (gca == NULL)
1355 return PyErr_NoMemory();
1356 return PyErr_NoMemory();
1356
1357
1357 for (i = 0; i < revcount; i++) {
1358 for (i = 0; i < revcount; i++) {
1358 if (revs[i] > maxrev)
1359 if (revs[i] > maxrev)
1359 maxrev = revs[i];
1360 maxrev = revs[i];
1360 }
1361 }
1361
1362
1362 seen = calloc(sizeof(*seen), maxrev + 1);
1363 seen = calloc(sizeof(*seen), maxrev + 1);
1363 if (seen == NULL) {
1364 if (seen == NULL) {
1364 Py_DECREF(gca);
1365 Py_DECREF(gca);
1365 return PyErr_NoMemory();
1366 return PyErr_NoMemory();
1366 }
1367 }
1367
1368
1368 for (i = 0; i < revcount; i++)
1369 for (i = 0; i < revcount; i++)
1369 seen[revs[i]] = 1ull << i;
1370 seen[revs[i]] = 1ull << i;
1370
1371
1371 interesting = revcount;
1372 interesting = revcount;
1372
1373
1373 for (v = maxrev; v >= 0 && interesting; v--) {
1374 for (v = maxrev; v >= 0 && interesting; v--) {
1374 bitmask sv = seen[v];
1375 bitmask sv = seen[v];
1375 int parents[2];
1376 int parents[2];
1376
1377
1377 if (!sv)
1378 if (!sv)
1378 continue;
1379 continue;
1379
1380
1380 if (sv < poison) {
1381 if (sv < poison) {
1381 interesting -= 1;
1382 interesting -= 1;
1382 if (sv == allseen) {
1383 if (sv == allseen) {
1383 PyObject *obj = PyInt_FromLong(v);
1384 PyObject *obj = PyInt_FromLong(v);
1384 if (obj == NULL)
1385 if (obj == NULL)
1385 goto bail;
1386 goto bail;
1386 if (PyList_Append(gca, obj) == -1) {
1387 if (PyList_Append(gca, obj) == -1) {
1387 Py_DECREF(obj);
1388 Py_DECREF(obj);
1388 goto bail;
1389 goto bail;
1389 }
1390 }
1390 sv |= poison;
1391 sv |= poison;
1391 for (i = 0; i < revcount; i++) {
1392 for (i = 0; i < revcount; i++) {
1392 if (revs[i] == v)
1393 if (revs[i] == v)
1393 goto done;
1394 goto done;
1394 }
1395 }
1395 }
1396 }
1396 }
1397 }
1397 if (index_get_parents(self, v, parents, maxrev) < 0)
1398 if (index_get_parents(self, v, parents, maxrev) < 0)
1398 goto bail;
1399 goto bail;
1399
1400
1400 for (i = 0; i < 2; i++) {
1401 for (i = 0; i < 2; i++) {
1401 int p = parents[i];
1402 int p = parents[i];
1402 if (p == -1)
1403 if (p == -1)
1403 continue;
1404 continue;
1404 sp = seen[p];
1405 sp = seen[p];
1405 if (sv < poison) {
1406 if (sv < poison) {
1406 if (sp == 0) {
1407 if (sp == 0) {
1407 seen[p] = sv;
1408 seen[p] = sv;
1408 interesting++;
1409 interesting++;
1409 }
1410 }
1410 else if (sp != sv)
1411 else if (sp != sv)
1411 seen[p] |= sv;
1412 seen[p] |= sv;
1412 } else {
1413 } else {
1413 if (sp && sp < poison)
1414 if (sp && sp < poison)
1414 interesting--;
1415 interesting--;
1415 seen[p] = sv;
1416 seen[p] = sv;
1416 }
1417 }
1417 }
1418 }
1418 }
1419 }
1419
1420
1420 done:
1421 done:
1421 free(seen);
1422 free(seen);
1422 return gca;
1423 return gca;
1423 bail:
1424 bail:
1424 free(seen);
1425 free(seen);
1425 Py_XDECREF(gca);
1426 Py_XDECREF(gca);
1426 return NULL;
1427 return NULL;
1427 }
1428 }
1428
1429
1429 /*
1430 /*
1430 * Given a disjoint set of revs, return the subset with the longest
1431 * Given a disjoint set of revs, return the subset with the longest
1431 * path to the root.
1432 * path to the root.
1432 */
1433 */
1433 static PyObject *find_deepest(indexObject *self, PyObject *revs)
1434 static PyObject *find_deepest(indexObject *self, PyObject *revs)
1434 {
1435 {
1435 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
1436 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
1436 static const Py_ssize_t capacity = 24;
1437 static const Py_ssize_t capacity = 24;
1437 int *depth, *interesting = NULL;
1438 int *depth, *interesting = NULL;
1438 int i, j, v, ninteresting;
1439 int i, j, v, ninteresting;
1439 PyObject *dict = NULL, *keys = NULL;
1440 PyObject *dict = NULL, *keys = NULL;
1440 long *seen = NULL;
1441 long *seen = NULL;
1441 int maxrev = -1;
1442 int maxrev = -1;
1442 long final;
1443 long final;
1443
1444
1444 if (revcount > capacity) {
1445 if (revcount > capacity) {
1445 PyErr_Format(PyExc_OverflowError,
1446 PyErr_Format(PyExc_OverflowError,
1446 "bitset size (%ld) > capacity (%ld)",
1447 "bitset size (%ld) > capacity (%ld)",
1447 (long)revcount, (long)capacity);
1448 (long)revcount, (long)capacity);
1448 return NULL;
1449 return NULL;
1449 }
1450 }
1450
1451
1451 for (i = 0; i < revcount; i++) {
1452 for (i = 0; i < revcount; i++) {
1452 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1453 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1453 if (n > maxrev)
1454 if (n > maxrev)
1454 maxrev = n;
1455 maxrev = n;
1455 }
1456 }
1456
1457
1457 depth = calloc(sizeof(*depth), maxrev + 1);
1458 depth = calloc(sizeof(*depth), maxrev + 1);
1458 if (depth == NULL)
1459 if (depth == NULL)
1459 return PyErr_NoMemory();
1460 return PyErr_NoMemory();
1460
1461
1461 seen = calloc(sizeof(*seen), maxrev + 1);
1462 seen = calloc(sizeof(*seen), maxrev + 1);
1462 if (seen == NULL) {
1463 if (seen == NULL) {
1463 PyErr_NoMemory();
1464 PyErr_NoMemory();
1464 goto bail;
1465 goto bail;
1465 }
1466 }
1466
1467
1467 interesting = calloc(sizeof(*interesting), 1 << revcount);
1468 interesting = calloc(sizeof(*interesting), 1 << revcount);
1468 if (interesting == NULL) {
1469 if (interesting == NULL) {
1469 PyErr_NoMemory();
1470 PyErr_NoMemory();
1470 goto bail;
1471 goto bail;
1471 }
1472 }
1472
1473
1473 if (PyList_Sort(revs) == -1)
1474 if (PyList_Sort(revs) == -1)
1474 goto bail;
1475 goto bail;
1475
1476
1476 for (i = 0; i < revcount; i++) {
1477 for (i = 0; i < revcount; i++) {
1477 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1478 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
1478 long b = 1l << i;
1479 long b = 1l << i;
1479 depth[n] = 1;
1480 depth[n] = 1;
1480 seen[n] = b;
1481 seen[n] = b;
1481 interesting[b] = 1;
1482 interesting[b] = 1;
1482 }
1483 }
1483
1484
1484 /* invariant: ninteresting is the number of non-zero entries in
1485 /* invariant: ninteresting is the number of non-zero entries in
1485 * interesting. */
1486 * interesting. */
1486 ninteresting = (int)revcount;
1487 ninteresting = (int)revcount;
1487
1488
1488 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
1489 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
1489 int dv = depth[v];
1490 int dv = depth[v];
1490 int parents[2];
1491 int parents[2];
1491 long sv;
1492 long sv;
1492
1493
1493 if (dv == 0)
1494 if (dv == 0)
1494 continue;
1495 continue;
1495
1496
1496 sv = seen[v];
1497 sv = seen[v];
1497 if (index_get_parents(self, v, parents, maxrev) < 0)
1498 if (index_get_parents(self, v, parents, maxrev) < 0)
1498 goto bail;
1499 goto bail;
1499
1500
1500 for (i = 0; i < 2; i++) {
1501 for (i = 0; i < 2; i++) {
1501 int p = parents[i];
1502 int p = parents[i];
1502 long sp;
1503 long sp;
1503 int dp;
1504 int dp;
1504
1505
1505 if (p == -1)
1506 if (p == -1)
1506 continue;
1507 continue;
1507
1508
1508 dp = depth[p];
1509 dp = depth[p];
1509 sp = seen[p];
1510 sp = seen[p];
1510 if (dp <= dv) {
1511 if (dp <= dv) {
1511 depth[p] = dv + 1;
1512 depth[p] = dv + 1;
1512 if (sp != sv) {
1513 if (sp != sv) {
1513 interesting[sv] += 1;
1514 interesting[sv] += 1;
1514 seen[p] = sv;
1515 seen[p] = sv;
1515 if (sp) {
1516 if (sp) {
1516 interesting[sp] -= 1;
1517 interesting[sp] -= 1;
1517 if (interesting[sp] == 0)
1518 if (interesting[sp] == 0)
1518 ninteresting -= 1;
1519 ninteresting -= 1;
1519 }
1520 }
1520 }
1521 }
1521 }
1522 }
1522 else if (dv == dp - 1) {
1523 else if (dv == dp - 1) {
1523 long nsp = sp | sv;
1524 long nsp = sp | sv;
1524 if (nsp == sp)
1525 if (nsp == sp)
1525 continue;
1526 continue;
1526 seen[p] = nsp;
1527 seen[p] = nsp;
1527 interesting[sp] -= 1;
1528 interesting[sp] -= 1;
1528 if (interesting[sp] == 0)
1529 if (interesting[sp] == 0)
1529 ninteresting -= 1;
1530 ninteresting -= 1;
1530 if (interesting[nsp] == 0)
1531 if (interesting[nsp] == 0)
1531 ninteresting += 1;
1532 ninteresting += 1;
1532 interesting[nsp] += 1;
1533 interesting[nsp] += 1;
1533 }
1534 }
1534 }
1535 }
1535 interesting[sv] -= 1;
1536 interesting[sv] -= 1;
1536 if (interesting[sv] == 0)
1537 if (interesting[sv] == 0)
1537 ninteresting -= 1;
1538 ninteresting -= 1;
1538 }
1539 }
1539
1540
1540 final = 0;
1541 final = 0;
1541 j = ninteresting;
1542 j = ninteresting;
1542 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
1543 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
1543 if (interesting[i] == 0)
1544 if (interesting[i] == 0)
1544 continue;
1545 continue;
1545 final |= i;
1546 final |= i;
1546 j -= 1;
1547 j -= 1;
1547 }
1548 }
1548 if (final == 0) {
1549 if (final == 0) {
1549 keys = PyList_New(0);
1550 keys = PyList_New(0);
1550 goto bail;
1551 goto bail;
1551 }
1552 }
1552
1553
1553 dict = PyDict_New();
1554 dict = PyDict_New();
1554 if (dict == NULL)
1555 if (dict == NULL)
1555 goto bail;
1556 goto bail;
1556
1557
1557 for (i = 0; i < revcount; i++) {
1558 for (i = 0; i < revcount; i++) {
1558 PyObject *key;
1559 PyObject *key;
1559
1560
1560 if ((final & (1 << i)) == 0)
1561 if ((final & (1 << i)) == 0)
1561 continue;
1562 continue;
1562
1563
1563 key = PyList_GET_ITEM(revs, i);
1564 key = PyList_GET_ITEM(revs, i);
1564 Py_INCREF(key);
1565 Py_INCREF(key);
1565 Py_INCREF(Py_None);
1566 Py_INCREF(Py_None);
1566 if (PyDict_SetItem(dict, key, Py_None) == -1) {
1567 if (PyDict_SetItem(dict, key, Py_None) == -1) {
1567 Py_DECREF(key);
1568 Py_DECREF(key);
1568 Py_DECREF(Py_None);
1569 Py_DECREF(Py_None);
1569 goto bail;
1570 goto bail;
1570 }
1571 }
1571 }
1572 }
1572
1573
1573 keys = PyDict_Keys(dict);
1574 keys = PyDict_Keys(dict);
1574
1575
1575 bail:
1576 bail:
1576 free(depth);
1577 free(depth);
1577 free(seen);
1578 free(seen);
1578 free(interesting);
1579 free(interesting);
1579 Py_XDECREF(dict);
1580 Py_XDECREF(dict);
1580
1581
1581 return keys;
1582 return keys;
1582 }
1583 }
1583
1584
1584 /*
1585 /*
1585 * Given a (possibly overlapping) set of revs, return all the
1586 * Given a (possibly overlapping) set of revs, return all the
1586 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
1587 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
1587 */
1588 */
1588 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
1589 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
1589 {
1590 {
1590 PyObject *ret = NULL;
1591 PyObject *ret = NULL;
1591 Py_ssize_t argcount, i, len;
1592 Py_ssize_t argcount, i, len;
1592 bitmask repeat = 0;
1593 bitmask repeat = 0;
1593 int revcount = 0;
1594 int revcount = 0;
1594 int *revs;
1595 int *revs;
1595
1596
1596 argcount = PySequence_Length(args);
1597 argcount = PySequence_Length(args);
1597 revs = PyMem_Malloc(argcount * sizeof(*revs));
1598 revs = PyMem_Malloc(argcount * sizeof(*revs));
1598 if (argcount > 0 && revs == NULL)
1599 if (argcount > 0 && revs == NULL)
1599 return PyErr_NoMemory();
1600 return PyErr_NoMemory();
1600 len = index_length(self) - 1;
1601 len = index_length(self) - 1;
1601
1602
1602 for (i = 0; i < argcount; i++) {
1603 for (i = 0; i < argcount; i++) {
1603 static const int capacity = 24;
1604 static const int capacity = 24;
1604 PyObject *obj = PySequence_GetItem(args, i);
1605 PyObject *obj = PySequence_GetItem(args, i);
1605 bitmask x;
1606 bitmask x;
1606 long val;
1607 long val;
1607
1608
1608 if (!PyInt_Check(obj)) {
1609 if (!PyInt_Check(obj)) {
1609 PyErr_SetString(PyExc_TypeError,
1610 PyErr_SetString(PyExc_TypeError,
1610 "arguments must all be ints");
1611 "arguments must all be ints");
1611 Py_DECREF(obj);
1612 Py_DECREF(obj);
1612 goto bail;
1613 goto bail;
1613 }
1614 }
1614 val = PyInt_AsLong(obj);
1615 val = PyInt_AsLong(obj);
1615 Py_DECREF(obj);
1616 Py_DECREF(obj);
1616 if (val == -1) {
1617 if (val == -1) {
1617 ret = PyList_New(0);
1618 ret = PyList_New(0);
1618 goto done;
1619 goto done;
1619 }
1620 }
1620 if (val < 0 || val >= len) {
1621 if (val < 0 || val >= len) {
1621 PyErr_SetString(PyExc_IndexError,
1622 PyErr_SetString(PyExc_IndexError,
1622 "index out of range");
1623 "index out of range");
1623 goto bail;
1624 goto bail;
1624 }
1625 }
1625 /* this cheesy bloom filter lets us avoid some more
1626 /* this cheesy bloom filter lets us avoid some more
1626 * expensive duplicate checks in the common set-is-disjoint
1627 * expensive duplicate checks in the common set-is-disjoint
1627 * case */
1628 * case */
1628 x = 1ull << (val & 0x3f);
1629 x = 1ull << (val & 0x3f);
1629 if (repeat & x) {
1630 if (repeat & x) {
1630 int k;
1631 int k;
1631 for (k = 0; k < revcount; k++) {
1632 for (k = 0; k < revcount; k++) {
1632 if (val == revs[k])
1633 if (val == revs[k])
1633 goto duplicate;
1634 goto duplicate;
1634 }
1635 }
1635 }
1636 }
1636 else repeat |= x;
1637 else repeat |= x;
1637 if (revcount >= capacity) {
1638 if (revcount >= capacity) {
1638 PyErr_Format(PyExc_OverflowError,
1639 PyErr_Format(PyExc_OverflowError,
1639 "bitset size (%d) > capacity (%d)",
1640 "bitset size (%d) > capacity (%d)",
1640 revcount, capacity);
1641 revcount, capacity);
1641 goto bail;
1642 goto bail;
1642 }
1643 }
1643 revs[revcount++] = (int)val;
1644 revs[revcount++] = (int)val;
1644 duplicate:;
1645 duplicate:;
1645 }
1646 }
1646
1647
1647 if (revcount == 0) {
1648 if (revcount == 0) {
1648 ret = PyList_New(0);
1649 ret = PyList_New(0);
1649 goto done;
1650 goto done;
1650 }
1651 }
1651 if (revcount == 1) {
1652 if (revcount == 1) {
1652 PyObject *obj;
1653 PyObject *obj;
1653 ret = PyList_New(1);
1654 ret = PyList_New(1);
1654 if (ret == NULL)
1655 if (ret == NULL)
1655 goto bail;
1656 goto bail;
1656 obj = PyInt_FromLong(revs[0]);
1657 obj = PyInt_FromLong(revs[0]);
1657 if (obj == NULL)
1658 if (obj == NULL)
1658 goto bail;
1659 goto bail;
1659 PyList_SET_ITEM(ret, 0, obj);
1660 PyList_SET_ITEM(ret, 0, obj);
1660 goto done;
1661 goto done;
1661 }
1662 }
1662
1663
1663 ret = find_gca_candidates(self, revs, revcount);
1664 ret = find_gca_candidates(self, revs, revcount);
1664 if (ret == NULL)
1665 if (ret == NULL)
1665 goto bail;
1666 goto bail;
1666
1667
1667 done:
1668 done:
1668 PyMem_Free(revs);
1669 PyMem_Free(revs);
1669 return ret;
1670 return ret;
1670
1671
1671 bail:
1672 bail:
1672 PyMem_Free(revs);
1673 PyMem_Free(revs);
1673 Py_XDECREF(ret);
1674 Py_XDECREF(ret);
1674 return NULL;
1675 return NULL;
1675 }
1676 }
1676
1677
1677 /*
1678 /*
1678 * Given a (possibly overlapping) set of revs, return the greatest
1679 * Given a (possibly overlapping) set of revs, return the greatest
1679 * common ancestors: those with the longest path to the root.
1680 * common ancestors: those with the longest path to the root.
1680 */
1681 */
1681 static PyObject *index_ancestors(indexObject *self, PyObject *args)
1682 static PyObject *index_ancestors(indexObject *self, PyObject *args)
1682 {
1683 {
1683 PyObject *ret;
1684 PyObject *ret;
1684 PyObject *gca = index_commonancestorsheads(self, args);
1685 PyObject *gca = index_commonancestorsheads(self, args);
1685 if (gca == NULL)
1686 if (gca == NULL)
1686 return NULL;
1687 return NULL;
1687
1688
1688 if (PyList_GET_SIZE(gca) <= 1) {
1689 if (PyList_GET_SIZE(gca) <= 1) {
1689 return gca;
1690 return gca;
1690 }
1691 }
1691
1692
1692 ret = find_deepest(self, gca);
1693 ret = find_deepest(self, gca);
1693 Py_DECREF(gca);
1694 Py_DECREF(gca);
1694 return ret;
1695 return ret;
1695 }
1696 }
1696
1697
1697 /*
1698 /*
1698 * Invalidate any trie entries introduced by added revs.
1699 * Invalidate any trie entries introduced by added revs.
1699 */
1700 */
1700 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1701 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
1701 {
1702 {
1702 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1703 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
1703
1704
1704 for (i = start; i < len; i++) {
1705 for (i = start; i < len; i++) {
1705 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1706 PyObject *tuple = PyList_GET_ITEM(self->added, i);
1706 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1707 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
1707
1708
1708 nt_insert(self, PyBytes_AS_STRING(node), -1);
1709 nt_insert(self, PyBytes_AS_STRING(node), -1);
1709 }
1710 }
1710
1711
1711 if (start == 0)
1712 if (start == 0)
1712 Py_CLEAR(self->added);
1713 Py_CLEAR(self->added);
1713 }
1714 }
1714
1715
1715 /*
1716 /*
1716 * Delete a numeric range of revs, which must be at the end of the
1717 * Delete a numeric range of revs, which must be at the end of the
1717 * range, but exclude the sentinel nullid entry.
1718 * range, but exclude the sentinel nullid entry.
1718 */
1719 */
1719 static int index_slice_del(indexObject *self, PyObject *item)
1720 static int index_slice_del(indexObject *self, PyObject *item)
1720 {
1721 {
1721 Py_ssize_t start, stop, step, slicelength;
1722 Py_ssize_t start, stop, step, slicelength;
1722 Py_ssize_t length = index_length(self);
1723 Py_ssize_t length = index_length(self);
1723 int ret = 0;
1724 int ret = 0;
1724
1725
1725 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
1726 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
1726 #ifdef IS_PY3K
1727 #ifdef IS_PY3K
1727 if (PySlice_GetIndicesEx(item, length,
1728 if (PySlice_GetIndicesEx(item, length,
1728 #else
1729 #else
1729 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1730 if (PySlice_GetIndicesEx((PySliceObject*)item, length,
1730 #endif
1731 #endif
1731 &start, &stop, &step, &slicelength) < 0)
1732 &start, &stop, &step, &slicelength) < 0)
1732 return -1;
1733 return -1;
1733
1734
1734 if (slicelength <= 0)
1735 if (slicelength <= 0)
1735 return 0;
1736 return 0;
1736
1737
1737 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1738 if ((step < 0 && start < stop) || (step > 0 && start > stop))
1738 stop = start;
1739 stop = start;
1739
1740
1740 if (step < 0) {
1741 if (step < 0) {
1741 stop = start + 1;
1742 stop = start + 1;
1742 start = stop + step*(slicelength - 1) - 1;
1743 start = stop + step*(slicelength - 1) - 1;
1743 step = -step;
1744 step = -step;
1744 }
1745 }
1745
1746
1746 if (step != 1) {
1747 if (step != 1) {
1747 PyErr_SetString(PyExc_ValueError,
1748 PyErr_SetString(PyExc_ValueError,
1748 "revlog index delete requires step size of 1");
1749 "revlog index delete requires step size of 1");
1749 return -1;
1750 return -1;
1750 }
1751 }
1751
1752
1752 if (stop != length - 1) {
1753 if (stop != length - 1) {
1753 PyErr_SetString(PyExc_IndexError,
1754 PyErr_SetString(PyExc_IndexError,
1754 "revlog index deletion indices are invalid");
1755 "revlog index deletion indices are invalid");
1755 return -1;
1756 return -1;
1756 }
1757 }
1757
1758
1758 if (start < self->length - 1) {
1759 if (start < self->length - 1) {
1759 if (self->nt) {
1760 if (self->nt) {
1760 Py_ssize_t i;
1761 Py_ssize_t i;
1761
1762
1762 for (i = start + 1; i < self->length - 1; i++) {
1763 for (i = start + 1; i < self->length - 1; i++) {
1763 const char *node = index_node(self, i);
1764 const char *node = index_node(self, i);
1764
1765
1765 if (node)
1766 if (node)
1766 nt_insert(self, node, -1);
1767 nt_insert(self, node, -1);
1767 }
1768 }
1768 if (self->added)
1769 if (self->added)
1769 nt_invalidate_added(self, 0);
1770 nt_invalidate_added(self, 0);
1770 if (self->ntrev > start)
1771 if (self->ntrev > start)
1771 self->ntrev = (int)start;
1772 self->ntrev = (int)start;
1772 }
1773 }
1773 self->length = start + 1;
1774 self->length = start + 1;
1774 if (start < self->raw_length) {
1775 if (start < self->raw_length) {
1775 if (self->cache) {
1776 if (self->cache) {
1776 Py_ssize_t i;
1777 Py_ssize_t i;
1777 for (i = start; i < self->raw_length; i++)
1778 for (i = start; i < self->raw_length; i++)
1778 Py_CLEAR(self->cache[i]);
1779 Py_CLEAR(self->cache[i]);
1779 }
1780 }
1780 self->raw_length = start;
1781 self->raw_length = start;
1781 }
1782 }
1782 goto done;
1783 goto done;
1783 }
1784 }
1784
1785
1785 if (self->nt) {
1786 if (self->nt) {
1786 nt_invalidate_added(self, start - self->length + 1);
1787 nt_invalidate_added(self, start - self->length + 1);
1787 if (self->ntrev > start)
1788 if (self->ntrev > start)
1788 self->ntrev = (int)start;
1789 self->ntrev = (int)start;
1789 }
1790 }
1790 if (self->added)
1791 if (self->added)
1791 ret = PyList_SetSlice(self->added, start - self->length + 1,
1792 ret = PyList_SetSlice(self->added, start - self->length + 1,
1792 PyList_GET_SIZE(self->added), NULL);
1793 PyList_GET_SIZE(self->added), NULL);
1793 done:
1794 done:
1794 Py_CLEAR(self->headrevs);
1795 Py_CLEAR(self->headrevs);
1795 return ret;
1796 return ret;
1796 }
1797 }
1797
1798
1798 /*
1799 /*
1799 * Supported ops:
1800 * Supported ops:
1800 *
1801 *
1801 * slice deletion
1802 * slice deletion
1802 * string assignment (extend node->rev mapping)
1803 * string assignment (extend node->rev mapping)
1803 * string deletion (shrink node->rev mapping)
1804 * string deletion (shrink node->rev mapping)
1804 */
1805 */
1805 static int index_assign_subscript(indexObject *self, PyObject *item,
1806 static int index_assign_subscript(indexObject *self, PyObject *item,
1806 PyObject *value)
1807 PyObject *value)
1807 {
1808 {
1808 char *node;
1809 char *node;
1809 Py_ssize_t nodelen;
1810 Py_ssize_t nodelen;
1810 long rev;
1811 long rev;
1811
1812
1812 if (PySlice_Check(item) && value == NULL)
1813 if (PySlice_Check(item) && value == NULL)
1813 return index_slice_del(self, item);
1814 return index_slice_del(self, item);
1814
1815
1815 if (node_check(item, &node, &nodelen) == -1)
1816 if (node_check(item, &node, &nodelen) == -1)
1816 return -1;
1817 return -1;
1817
1818
1818 if (value == NULL)
1819 if (value == NULL)
1819 return self->nt ? nt_insert(self, node, -1) : 0;
1820 return self->nt ? nt_insert(self, node, -1) : 0;
1820 rev = PyInt_AsLong(value);
1821 rev = PyInt_AsLong(value);
1821 if (rev > INT_MAX || rev < 0) {
1822 if (rev > INT_MAX || rev < 0) {
1822 if (!PyErr_Occurred())
1823 if (!PyErr_Occurred())
1823 PyErr_SetString(PyExc_ValueError, "rev out of range");
1824 PyErr_SetString(PyExc_ValueError, "rev out of range");
1824 return -1;
1825 return -1;
1825 }
1826 }
1826
1827
1827 if (nt_init(self) == -1)
1828 if (nt_init(self) == -1)
1828 return -1;
1829 return -1;
1829 return nt_insert(self, node, (int)rev);
1830 return nt_insert(self, node, (int)rev);
1830 }
1831 }
1831
1832
1832 /*
1833 /*
1833 * Find all RevlogNG entries in an index that has inline data. Update
1834 * Find all RevlogNG entries in an index that has inline data. Update
1834 * the optional "offsets" table with those entries.
1835 * the optional "offsets" table with those entries.
1835 */
1836 */
1836 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
1837 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
1837 {
1838 {
1838 const char *data = (const char *)self->buf.buf;
1839 const char *data = (const char *)self->buf.buf;
1839 Py_ssize_t pos = 0;
1840 Py_ssize_t pos = 0;
1840 Py_ssize_t end = self->buf.len;
1841 Py_ssize_t end = self->buf.len;
1841 long incr = v1_hdrsize;
1842 long incr = v1_hdrsize;
1842 Py_ssize_t len = 0;
1843 Py_ssize_t len = 0;
1843
1844
1844 while (pos + v1_hdrsize <= end && pos >= 0) {
1845 while (pos + v1_hdrsize <= end && pos >= 0) {
1845 uint32_t comp_len;
1846 uint32_t comp_len;
1846 /* 3rd element of header is length of compressed inline data */
1847 /* 3rd element of header is length of compressed inline data */
1847 comp_len = getbe32(data + pos + 8);
1848 comp_len = getbe32(data + pos + 8);
1848 incr = v1_hdrsize + comp_len;
1849 incr = v1_hdrsize + comp_len;
1849 if (offsets)
1850 if (offsets)
1850 offsets[len] = data + pos;
1851 offsets[len] = data + pos;
1851 len++;
1852 len++;
1852 pos += incr;
1853 pos += incr;
1853 }
1854 }
1854
1855
1855 if (pos != end) {
1856 if (pos != end) {
1856 if (!PyErr_Occurred())
1857 if (!PyErr_Occurred())
1857 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1858 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1858 return -1;
1859 return -1;
1859 }
1860 }
1860
1861
1861 return len;
1862 return len;
1862 }
1863 }
1863
1864
1864 static int index_init(indexObject *self, PyObject *args)
1865 static int index_init(indexObject *self, PyObject *args)
1865 {
1866 {
1866 PyObject *data_obj, *inlined_obj;
1867 PyObject *data_obj, *inlined_obj;
1867 Py_ssize_t size;
1868 Py_ssize_t size;
1868
1869
1869 /* Initialize before argument-checking to avoid index_dealloc() crash. */
1870 /* Initialize before argument-checking to avoid index_dealloc() crash. */
1870 self->raw_length = 0;
1871 self->raw_length = 0;
1871 self->added = NULL;
1872 self->added = NULL;
1872 self->cache = NULL;
1873 self->cache = NULL;
1873 self->data = NULL;
1874 self->data = NULL;
1874 memset(&self->buf, 0, sizeof(self->buf));
1875 memset(&self->buf, 0, sizeof(self->buf));
1875 self->headrevs = NULL;
1876 self->headrevs = NULL;
1876 self->filteredrevs = Py_None;
1877 self->filteredrevs = Py_None;
1877 Py_INCREF(Py_None);
1878 Py_INCREF(Py_None);
1878 self->nt = NULL;
1879 self->nt = NULL;
1879 self->offsets = NULL;
1880 self->offsets = NULL;
1880
1881
1881 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1882 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
1882 return -1;
1883 return -1;
1883 if (!PyObject_CheckBuffer(data_obj)) {
1884 if (!PyObject_CheckBuffer(data_obj)) {
1884 PyErr_SetString(PyExc_TypeError,
1885 PyErr_SetString(PyExc_TypeError,
1885 "data does not support buffer interface");
1886 "data does not support buffer interface");
1886 return -1;
1887 return -1;
1887 }
1888 }
1888
1889
1889 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
1890 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
1890 return -1;
1891 return -1;
1891 size = self->buf.len;
1892 size = self->buf.len;
1892
1893
1893 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1894 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
1894 self->data = data_obj;
1895 self->data = data_obj;
1895
1896
1896 self->ntlength = self->ntcapacity = 0;
1897 self->ntlength = self->ntcapacity = 0;
1897 self->ntdepth = self->ntsplits = 0;
1898 self->ntdepth = self->ntsplits = 0;
1898 self->ntlookups = self->ntmisses = 0;
1899 self->ntlookups = self->ntmisses = 0;
1899 self->ntrev = -1;
1900 self->ntrev = -1;
1900 Py_INCREF(self->data);
1901 Py_INCREF(self->data);
1901
1902
1902 if (self->inlined) {
1903 if (self->inlined) {
1903 Py_ssize_t len = inline_scan(self, NULL);
1904 Py_ssize_t len = inline_scan(self, NULL);
1904 if (len == -1)
1905 if (len == -1)
1905 goto bail;
1906 goto bail;
1906 self->raw_length = len;
1907 self->raw_length = len;
1907 self->length = len + 1;
1908 self->length = len + 1;
1908 } else {
1909 } else {
1909 if (size % v1_hdrsize) {
1910 if (size % v1_hdrsize) {
1910 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1911 PyErr_SetString(PyExc_ValueError, "corrupt index file");
1911 goto bail;
1912 goto bail;
1912 }
1913 }
1913 self->raw_length = size / v1_hdrsize;
1914 self->raw_length = size / v1_hdrsize;
1914 self->length = self->raw_length + 1;
1915 self->length = self->raw_length + 1;
1915 }
1916 }
1916
1917
1917 return 0;
1918 return 0;
1918 bail:
1919 bail:
1919 return -1;
1920 return -1;
1920 }
1921 }
1921
1922
1922 static PyObject *index_nodemap(indexObject *self)
1923 static PyObject *index_nodemap(indexObject *self)
1923 {
1924 {
1924 Py_INCREF(self);
1925 Py_INCREF(self);
1925 return (PyObject *)self;
1926 return (PyObject *)self;
1926 }
1927 }
1927
1928
1928 static void index_dealloc(indexObject *self)
1929 static void index_dealloc(indexObject *self)
1929 {
1930 {
1930 _index_clearcaches(self);
1931 _index_clearcaches(self);
1931 Py_XDECREF(self->filteredrevs);
1932 Py_XDECREF(self->filteredrevs);
1932 if (self->buf.buf) {
1933 if (self->buf.buf) {
1933 PyBuffer_Release(&self->buf);
1934 PyBuffer_Release(&self->buf);
1934 memset(&self->buf, 0, sizeof(self->buf));
1935 memset(&self->buf, 0, sizeof(self->buf));
1935 }
1936 }
1936 Py_XDECREF(self->data);
1937 Py_XDECREF(self->data);
1937 Py_XDECREF(self->added);
1938 Py_XDECREF(self->added);
1938 PyObject_Del(self);
1939 PyObject_Del(self);
1939 }
1940 }
1940
1941
1941 static PySequenceMethods index_sequence_methods = {
1942 static PySequenceMethods index_sequence_methods = {
1942 (lenfunc)index_length, /* sq_length */
1943 (lenfunc)index_length, /* sq_length */
1943 0, /* sq_concat */
1944 0, /* sq_concat */
1944 0, /* sq_repeat */
1945 0, /* sq_repeat */
1945 (ssizeargfunc)index_get, /* sq_item */
1946 (ssizeargfunc)index_get, /* sq_item */
1946 0, /* sq_slice */
1947 0, /* sq_slice */
1947 0, /* sq_ass_item */
1948 0, /* sq_ass_item */
1948 0, /* sq_ass_slice */
1949 0, /* sq_ass_slice */
1949 (objobjproc)index_contains, /* sq_contains */
1950 (objobjproc)index_contains, /* sq_contains */
1950 };
1951 };
1951
1952
1952 static PyMappingMethods index_mapping_methods = {
1953 static PyMappingMethods index_mapping_methods = {
1953 (lenfunc)index_length, /* mp_length */
1954 (lenfunc)index_length, /* mp_length */
1954 (binaryfunc)index_getitem, /* mp_subscript */
1955 (binaryfunc)index_getitem, /* mp_subscript */
1955 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1956 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
1956 };
1957 };
1957
1958
1958 static PyMethodDef index_methods[] = {
1959 static PyMethodDef index_methods[] = {
1959 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
1960 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
1960 "return the gca set of the given revs"},
1961 "return the gca set of the given revs"},
1961 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
1962 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
1962 METH_VARARGS,
1963 METH_VARARGS,
1963 "return the heads of the common ancestors of the given revs"},
1964 "return the heads of the common ancestors of the given revs"},
1964 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1965 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
1965 "clear the index caches"},
1966 "clear the index caches"},
1966 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1967 {"get", (PyCFunction)index_m_get, METH_VARARGS,
1967 "get an index entry"},
1968 "get an index entry"},
1968 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
1969 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
1969 METH_VARARGS, "compute phases"},
1970 METH_VARARGS, "compute phases"},
1970 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
1971 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
1971 "reachableroots"},
1972 "reachableroots"},
1972 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
1973 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
1973 "get head revisions"}, /* Can do filtering since 3.2 */
1974 "get head revisions"}, /* Can do filtering since 3.2 */
1974 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
1975 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
1975 "get filtered head revisions"}, /* Can always do filtering */
1976 "get filtered head revisions"}, /* Can always do filtering */
1976 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
1977 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
1977 "determine revisions with deltas to reconstruct fulltext"},
1978 "determine revisions with deltas to reconstruct fulltext"},
1978 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1979 {"insert", (PyCFunction)index_insert, METH_VARARGS,
1979 "insert an index entry"},
1980 "insert an index entry"},
1980 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1981 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
1981 "match a potentially ambiguous node ID"},
1982 "match a potentially ambiguous node ID"},
1982 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1983 {"stats", (PyCFunction)index_stats, METH_NOARGS,
1983 "stats for the index"},
1984 "stats for the index"},
1984 {NULL} /* Sentinel */
1985 {NULL} /* Sentinel */
1985 };
1986 };
1986
1987
1987 static PyGetSetDef index_getset[] = {
1988 static PyGetSetDef index_getset[] = {
1988 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1989 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
1989 {NULL} /* Sentinel */
1990 {NULL} /* Sentinel */
1990 };
1991 };
1991
1992
1992 static PyTypeObject indexType = {
1993 static PyTypeObject indexType = {
1993 PyVarObject_HEAD_INIT(NULL, 0)
1994 PyVarObject_HEAD_INIT(NULL, 0)
1994 "parsers.index", /* tp_name */
1995 "parsers.index", /* tp_name */
1995 sizeof(indexObject), /* tp_basicsize */
1996 sizeof(indexObject), /* tp_basicsize */
1996 0, /* tp_itemsize */
1997 0, /* tp_itemsize */
1997 (destructor)index_dealloc, /* tp_dealloc */
1998 (destructor)index_dealloc, /* tp_dealloc */
1998 0, /* tp_print */
1999 0, /* tp_print */
1999 0, /* tp_getattr */
2000 0, /* tp_getattr */
2000 0, /* tp_setattr */
2001 0, /* tp_setattr */
2001 0, /* tp_compare */
2002 0, /* tp_compare */
2002 0, /* tp_repr */
2003 0, /* tp_repr */
2003 0, /* tp_as_number */
2004 0, /* tp_as_number */
2004 &index_sequence_methods, /* tp_as_sequence */
2005 &index_sequence_methods, /* tp_as_sequence */
2005 &index_mapping_methods, /* tp_as_mapping */
2006 &index_mapping_methods, /* tp_as_mapping */
2006 0, /* tp_hash */
2007 0, /* tp_hash */
2007 0, /* tp_call */
2008 0, /* tp_call */
2008 0, /* tp_str */
2009 0, /* tp_str */
2009 0, /* tp_getattro */
2010 0, /* tp_getattro */
2010 0, /* tp_setattro */
2011 0, /* tp_setattro */
2011 0, /* tp_as_buffer */
2012 0, /* tp_as_buffer */
2012 Py_TPFLAGS_DEFAULT, /* tp_flags */
2013 Py_TPFLAGS_DEFAULT, /* tp_flags */
2013 "revlog index", /* tp_doc */
2014 "revlog index", /* tp_doc */
2014 0, /* tp_traverse */
2015 0, /* tp_traverse */
2015 0, /* tp_clear */
2016 0, /* tp_clear */
2016 0, /* tp_richcompare */
2017 0, /* tp_richcompare */
2017 0, /* tp_weaklistoffset */
2018 0, /* tp_weaklistoffset */
2018 0, /* tp_iter */
2019 0, /* tp_iter */
2019 0, /* tp_iternext */
2020 0, /* tp_iternext */
2020 index_methods, /* tp_methods */
2021 index_methods, /* tp_methods */
2021 0, /* tp_members */
2022 0, /* tp_members */
2022 index_getset, /* tp_getset */
2023 index_getset, /* tp_getset */
2023 0, /* tp_base */
2024 0, /* tp_base */
2024 0, /* tp_dict */
2025 0, /* tp_dict */
2025 0, /* tp_descr_get */
2026 0, /* tp_descr_get */
2026 0, /* tp_descr_set */
2027 0, /* tp_descr_set */
2027 0, /* tp_dictoffset */
2028 0, /* tp_dictoffset */
2028 (initproc)index_init, /* tp_init */
2029 (initproc)index_init, /* tp_init */
2029 0, /* tp_alloc */
2030 0, /* tp_alloc */
2030 };
2031 };
2031
2032
2032 /*
2033 /*
2033 * returns a tuple of the form (index, index, cache) with elements as
2034 * returns a tuple of the form (index, index, cache) with elements as
2034 * follows:
2035 * follows:
2035 *
2036 *
2036 * index: an index object that lazily parses RevlogNG records
2037 * index: an index object that lazily parses RevlogNG records
2037 * cache: if data is inlined, a tuple (0, index_file_content), else None
2038 * cache: if data is inlined, a tuple (0, index_file_content), else None
2038 * index_file_content could be a string, or a buffer
2039 * index_file_content could be a string, or a buffer
2039 *
2040 *
2040 * added complications are for backwards compatibility
2041 * added complications are for backwards compatibility
2041 */
2042 */
2042 PyObject *parse_index2(PyObject *self, PyObject *args)
2043 PyObject *parse_index2(PyObject *self, PyObject *args)
2043 {
2044 {
2044 PyObject *tuple = NULL, *cache = NULL;
2045 PyObject *tuple = NULL, *cache = NULL;
2045 indexObject *idx;
2046 indexObject *idx;
2046 int ret;
2047 int ret;
2047
2048
2048 idx = PyObject_New(indexObject, &indexType);
2049 idx = PyObject_New(indexObject, &indexType);
2049 if (idx == NULL)
2050 if (idx == NULL)
2050 goto bail;
2051 goto bail;
2051
2052
2052 ret = index_init(idx, args);
2053 ret = index_init(idx, args);
2053 if (ret == -1)
2054 if (ret == -1)
2054 goto bail;
2055 goto bail;
2055
2056
2056 if (idx->inlined) {
2057 if (idx->inlined) {
2057 cache = Py_BuildValue("iO", 0, idx->data);
2058 cache = Py_BuildValue("iO", 0, idx->data);
2058 if (cache == NULL)
2059 if (cache == NULL)
2059 goto bail;
2060 goto bail;
2060 } else {
2061 } else {
2061 cache = Py_None;
2062 cache = Py_None;
2062 Py_INCREF(cache);
2063 Py_INCREF(cache);
2063 }
2064 }
2064
2065
2065 tuple = Py_BuildValue("NN", idx, cache);
2066 tuple = Py_BuildValue("NN", idx, cache);
2066 if (!tuple)
2067 if (!tuple)
2067 goto bail;
2068 goto bail;
2068 return tuple;
2069 return tuple;
2069
2070
2070 bail:
2071 bail:
2071 Py_XDECREF(idx);
2072 Py_XDECREF(idx);
2072 Py_XDECREF(cache);
2073 Py_XDECREF(cache);
2073 Py_XDECREF(tuple);
2074 Py_XDECREF(tuple);
2074 return NULL;
2075 return NULL;
2075 }
2076 }
2076
2077
2077 void revlog_module_init(PyObject *mod)
2078 void revlog_module_init(PyObject *mod)
2078 {
2079 {
2079 indexType.tp_new = PyType_GenericNew;
2080 indexType.tp_new = PyType_GenericNew;
2080 if (PyType_Ready(&indexType) < 0)
2081 if (PyType_Ready(&indexType) < 0)
2081 return;
2082 return;
2082 Py_INCREF(&indexType);
2083 Py_INCREF(&indexType);
2083 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
2084 PyModule_AddObject(mod, "index", (PyObject *)&indexType);
2084
2085
2085 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
2086 nullentry = Py_BuildValue("iiiiiiis#", 0, 0, 0,
2086 -1, -1, -1, -1, nullid, 20);
2087 -1, -1, -1, -1, nullid, 20);
2087 if (nullentry)
2088 if (nullentry)
2088 PyObject_GC_UnTrack(nullentry);
2089 PyObject_GC_UnTrack(nullentry);
2089 }
2090 }
@@ -1,88 +1,50 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 typedef struct {
17 typedef struct {
18 PyObject_HEAD
18 PyObject_HEAD
19 char state;
19 char state;
20 int mode;
20 int mode;
21 int size;
21 int size;
22 int mtime;
22 int mtime;
23 } dirstateTupleObject;
23 } dirstateTupleObject;
24
24
25 extern PyTypeObject dirstateTupleType;
25 extern PyTypeObject dirstateTupleType;
26 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateTupleType)
26 #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateTupleType)
27
27
28 /* This should be kept in sync with normcasespecs in encoding.py. */
29 enum normcase_spec {
30 NORMCASE_LOWER = -1,
31 NORMCASE_UPPER = 1,
32 NORMCASE_OTHER = 0
33 };
34
35 #define MIN(a, b) (((a)<(b))?(a):(b))
28 #define MIN(a, b) (((a)<(b))?(a):(b))
36 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
29 /* VC9 doesn't include bool and lacks stdbool.h based on my searching */
37 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
30 #if defined(_MSC_VER) || __STDC_VERSION__ < 199901L
38 #define true 1
31 #define true 1
39 #define false 0
32 #define false 0
40 typedef unsigned char bool;
33 typedef unsigned char bool;
41 #else
34 #else
42 #include <stdbool.h>
35 #include <stdbool.h>
43 #endif
36 #endif
44
37
45 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
38 static inline PyObject *_dict_new_presized(Py_ssize_t expected_size)
46 {
39 {
47 /* _PyDict_NewPresized expects a minused parameter, but it actually
40 /* _PyDict_NewPresized expects a minused parameter, but it actually
48 creates a dictionary that's the nearest power of two bigger than the
41 creates a dictionary that's the nearest power of two bigger than the
49 parameter. For example, with the initial minused = 1000, the
42 parameter. For example, with the initial minused = 1000, the
50 dictionary created has size 1024. Of course in a lot of cases that
43 dictionary created has size 1024. Of course in a lot of cases that
51 can be greater than the maximum load factor Python's dict object
44 can be greater than the maximum load factor Python's dict object
52 expects (= 2/3), so as soon as we cross the threshold we'll resize
45 expects (= 2/3), so as soon as we cross the threshold we'll resize
53 anyway. So create a dictionary that's at least 3/2 the size. */
46 anyway. So create a dictionary that's at least 3/2 the size. */
54 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
47 return _PyDict_NewPresized(((1 + expected_size) / 2) * 3);
55 }
48 }
56
49
57 static const int8_t hextable[256] = {
58 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
59 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
60 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
61 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 0-9 */
62 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A-F */
63 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
64 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a-f */
65 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
66 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
67 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
68 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
70 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
71 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
72 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
73 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
74 };
75
76 static inline int hexdigit(const char *p, Py_ssize_t off)
77 {
78 int8_t val = hextable[(unsigned char)p[off]];
79
80 if (val >= 0) {
81 return val;
82 }
83
84 PyErr_SetString(PyExc_ValueError, "input contains non-hex character");
85 return 0;
86 }
87
88 #endif /* _HG_UTIL_H_ */
50 #endif /* _HG_UTIL_H_ */
@@ -1,953 +1,953 b''
1 #
1 #
2 # This is the mercurial setup script.
2 # This is the mercurial setup script.
3 #
3 #
4 # 'python setup.py install', or
4 # 'python setup.py install', or
5 # 'python setup.py --help' for more options
5 # 'python setup.py --help' for more options
6
6
7 import os
7 import os
8
8
9 supportedpy = '~= 2.7'
9 supportedpy = '~= 2.7'
10 if os.environ.get('HGALLOWPYTHON3', ''):
10 if os.environ.get('HGALLOWPYTHON3', ''):
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
11 # Mercurial will never work on Python 3 before 3.5 due to a lack
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
12 # of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
13 # due to a bug in % formatting in bytestrings.
13 # due to a bug in % formatting in bytestrings.
14 #
14 #
15 # TODO: when we actually work on Python 3, use this string as the
15 # TODO: when we actually work on Python 3, use this string as the
16 # actual supportedpy string.
16 # actual supportedpy string.
17 supportedpy = ','.join([
17 supportedpy = ','.join([
18 '>=2.7',
18 '>=2.7',
19 '!=3.0.*',
19 '!=3.0.*',
20 '!=3.1.*',
20 '!=3.1.*',
21 '!=3.2.*',
21 '!=3.2.*',
22 '!=3.3.*',
22 '!=3.3.*',
23 '!=3.4.*',
23 '!=3.4.*',
24 '!=3.6.0',
24 '!=3.6.0',
25 '!=3.6.1',
25 '!=3.6.1',
26 ])
26 ])
27
27
28 import sys, platform
28 import sys, platform
29 if sys.version_info[0] >= 3:
29 if sys.version_info[0] >= 3:
30 printf = eval('print')
30 printf = eval('print')
31 libdir_escape = 'unicode_escape'
31 libdir_escape = 'unicode_escape'
32 else:
32 else:
33 libdir_escape = 'string_escape'
33 libdir_escape = 'string_escape'
34 def printf(*args, **kwargs):
34 def printf(*args, **kwargs):
35 f = kwargs.get('file', sys.stdout)
35 f = kwargs.get('file', sys.stdout)
36 end = kwargs.get('end', '\n')
36 end = kwargs.get('end', '\n')
37 f.write(b' '.join(args) + end)
37 f.write(b' '.join(args) + end)
38
38
39 # Attempt to guide users to a modern pip - this means that 2.6 users
39 # Attempt to guide users to a modern pip - this means that 2.6 users
40 # should have a chance of getting a 4.2 release, and when we ratchet
40 # should have a chance of getting a 4.2 release, and when we ratchet
41 # the version requirement forward again hopefully everyone will get
41 # the version requirement forward again hopefully everyone will get
42 # something that works for them.
42 # something that works for them.
43 if sys.version_info < (2, 7, 0, 'final'):
43 if sys.version_info < (2, 7, 0, 'final'):
44 pip_message = ('This may be due to an out of date pip. '
44 pip_message = ('This may be due to an out of date pip. '
45 'Make sure you have pip >= 9.0.1.')
45 'Make sure you have pip >= 9.0.1.')
46 try:
46 try:
47 import pip
47 import pip
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
48 pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
49 if pip_version < (9, 0, 1) :
49 if pip_version < (9, 0, 1) :
50 pip_message = (
50 pip_message = (
51 'Your pip version is out of date, please install '
51 'Your pip version is out of date, please install '
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
52 'pip >= 9.0.1. pip {} detected.'.format(pip.__version__))
53 else:
53 else:
54 # pip is new enough - it must be something else
54 # pip is new enough - it must be something else
55 pip_message = ''
55 pip_message = ''
56 except Exception:
56 except Exception:
57 pass
57 pass
58 error = """
58 error = """
59 Mercurial does not support Python older than 2.7.
59 Mercurial does not support Python older than 2.7.
60 Python {py} detected.
60 Python {py} detected.
61 {pip}
61 {pip}
62 """.format(py=sys.version_info, pip=pip_message)
62 """.format(py=sys.version_info, pip=pip_message)
63 printf(error, file=sys.stderr)
63 printf(error, file=sys.stderr)
64 sys.exit(1)
64 sys.exit(1)
65
65
66 # Solaris Python packaging brain damage
66 # Solaris Python packaging brain damage
67 try:
67 try:
68 import hashlib
68 import hashlib
69 sha = hashlib.sha1()
69 sha = hashlib.sha1()
70 except ImportError:
70 except ImportError:
71 try:
71 try:
72 import sha
72 import sha
73 sha.sha # silence unused import warning
73 sha.sha # silence unused import warning
74 except ImportError:
74 except ImportError:
75 raise SystemExit(
75 raise SystemExit(
76 "Couldn't import standard hashlib (incomplete Python install).")
76 "Couldn't import standard hashlib (incomplete Python install).")
77
77
78 try:
78 try:
79 import zlib
79 import zlib
80 zlib.compressobj # silence unused import warning
80 zlib.compressobj # silence unused import warning
81 except ImportError:
81 except ImportError:
82 raise SystemExit(
82 raise SystemExit(
83 "Couldn't import standard zlib (incomplete Python install).")
83 "Couldn't import standard zlib (incomplete Python install).")
84
84
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
85 # The base IronPython distribution (as of 2.7.1) doesn't support bz2
86 isironpython = False
86 isironpython = False
87 try:
87 try:
88 isironpython = (platform.python_implementation()
88 isironpython = (platform.python_implementation()
89 .lower().find("ironpython") != -1)
89 .lower().find("ironpython") != -1)
90 except AttributeError:
90 except AttributeError:
91 pass
91 pass
92
92
93 if isironpython:
93 if isironpython:
94 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
94 sys.stderr.write("warning: IronPython detected (no bz2 support)\n")
95 else:
95 else:
96 try:
96 try:
97 import bz2
97 import bz2
98 bz2.BZ2Compressor # silence unused import warning
98 bz2.BZ2Compressor # silence unused import warning
99 except ImportError:
99 except ImportError:
100 raise SystemExit(
100 raise SystemExit(
101 "Couldn't import standard bz2 (incomplete Python install).")
101 "Couldn't import standard bz2 (incomplete Python install).")
102
102
103 ispypy = "PyPy" in sys.version
103 ispypy = "PyPy" in sys.version
104
104
105 import ctypes
105 import ctypes
106 import stat, subprocess, time
106 import stat, subprocess, time
107 import re
107 import re
108 import shutil
108 import shutil
109 import tempfile
109 import tempfile
110 from distutils import log
110 from distutils import log
111 # We have issues with setuptools on some platforms and builders. Until
111 # We have issues with setuptools on some platforms and builders. Until
112 # those are resolved, setuptools is opt-in except for platforms where
112 # those are resolved, setuptools is opt-in except for platforms where
113 # we don't have issues.
113 # we don't have issues.
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
114 issetuptools = (os.name == 'nt' or 'FORCE_SETUPTOOLS' in os.environ)
115 if issetuptools:
115 if issetuptools:
116 from setuptools import setup
116 from setuptools import setup
117 else:
117 else:
118 from distutils.core import setup
118 from distutils.core import setup
119 from distutils.ccompiler import new_compiler
119 from distutils.ccompiler import new_compiler
120 from distutils.core import Command, Extension
120 from distutils.core import Command, Extension
121 from distutils.dist import Distribution
121 from distutils.dist import Distribution
122 from distutils.command.build import build
122 from distutils.command.build import build
123 from distutils.command.build_ext import build_ext
123 from distutils.command.build_ext import build_ext
124 from distutils.command.build_py import build_py
124 from distutils.command.build_py import build_py
125 from distutils.command.build_scripts import build_scripts
125 from distutils.command.build_scripts import build_scripts
126 from distutils.command.install import install
126 from distutils.command.install import install
127 from distutils.command.install_lib import install_lib
127 from distutils.command.install_lib import install_lib
128 from distutils.command.install_scripts import install_scripts
128 from distutils.command.install_scripts import install_scripts
129 from distutils.spawn import spawn, find_executable
129 from distutils.spawn import spawn, find_executable
130 from distutils import file_util
130 from distutils import file_util
131 from distutils.errors import (
131 from distutils.errors import (
132 CCompilerError,
132 CCompilerError,
133 DistutilsError,
133 DistutilsError,
134 DistutilsExecError,
134 DistutilsExecError,
135 )
135 )
136 from distutils.sysconfig import get_python_inc, get_config_var
136 from distutils.sysconfig import get_python_inc, get_config_var
137 from distutils.version import StrictVersion
137 from distutils.version import StrictVersion
138
138
139 scripts = ['hg']
139 scripts = ['hg']
140 if os.name == 'nt':
140 if os.name == 'nt':
141 # We remove hg.bat if we are able to build hg.exe.
141 # We remove hg.bat if we are able to build hg.exe.
142 scripts.append('contrib/win32/hg.bat')
142 scripts.append('contrib/win32/hg.bat')
143
143
144 def cancompile(cc, code):
144 def cancompile(cc, code):
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
145 tmpdir = tempfile.mkdtemp(prefix='hg-install-')
146 devnull = oldstderr = None
146 devnull = oldstderr = None
147 try:
147 try:
148 fname = os.path.join(tmpdir, 'testcomp.c')
148 fname = os.path.join(tmpdir, 'testcomp.c')
149 f = open(fname, 'w')
149 f = open(fname, 'w')
150 f.write(code)
150 f.write(code)
151 f.close()
151 f.close()
152 # Redirect stderr to /dev/null to hide any error messages
152 # Redirect stderr to /dev/null to hide any error messages
153 # from the compiler.
153 # from the compiler.
154 # This will have to be changed if we ever have to check
154 # This will have to be changed if we ever have to check
155 # for a function on Windows.
155 # for a function on Windows.
156 devnull = open('/dev/null', 'w')
156 devnull = open('/dev/null', 'w')
157 oldstderr = os.dup(sys.stderr.fileno())
157 oldstderr = os.dup(sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
158 os.dup2(devnull.fileno(), sys.stderr.fileno())
159 objects = cc.compile([fname], output_dir=tmpdir)
159 objects = cc.compile([fname], output_dir=tmpdir)
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
160 cc.link_executable(objects, os.path.join(tmpdir, "a.out"))
161 return True
161 return True
162 except Exception:
162 except Exception:
163 return False
163 return False
164 finally:
164 finally:
165 if oldstderr is not None:
165 if oldstderr is not None:
166 os.dup2(oldstderr, sys.stderr.fileno())
166 os.dup2(oldstderr, sys.stderr.fileno())
167 if devnull is not None:
167 if devnull is not None:
168 devnull.close()
168 devnull.close()
169 shutil.rmtree(tmpdir)
169 shutil.rmtree(tmpdir)
170
170
171 # simplified version of distutils.ccompiler.CCompiler.has_function
171 # simplified version of distutils.ccompiler.CCompiler.has_function
172 # that actually removes its temporary files.
172 # that actually removes its temporary files.
173 def hasfunction(cc, funcname):
173 def hasfunction(cc, funcname):
174 code = 'int main(void) { %s(); }\n' % funcname
174 code = 'int main(void) { %s(); }\n' % funcname
175 return cancompile(cc, code)
175 return cancompile(cc, code)
176
176
177 def hasheader(cc, headername):
177 def hasheader(cc, headername):
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
178 code = '#include <%s>\nint main(void) { return 0; }\n' % headername
179 return cancompile(cc, code)
179 return cancompile(cc, code)
180
180
181 # py2exe needs to be installed to work
181 # py2exe needs to be installed to work
182 try:
182 try:
183 import py2exe
183 import py2exe
184 py2exe.Distribution # silence unused import warning
184 py2exe.Distribution # silence unused import warning
185 py2exeloaded = True
185 py2exeloaded = True
186 # import py2exe's patched Distribution class
186 # import py2exe's patched Distribution class
187 from distutils.core import Distribution
187 from distutils.core import Distribution
188 except ImportError:
188 except ImportError:
189 py2exeloaded = False
189 py2exeloaded = False
190
190
191 def runcmd(cmd, env):
191 def runcmd(cmd, env):
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
192 p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
193 stderr=subprocess.PIPE, env=env)
193 stderr=subprocess.PIPE, env=env)
194 out, err = p.communicate()
194 out, err = p.communicate()
195 return p.returncode, out, err
195 return p.returncode, out, err
196
196
197 class hgcommand(object):
197 class hgcommand(object):
198 def __init__(self, cmd, env):
198 def __init__(self, cmd, env):
199 self.cmd = cmd
199 self.cmd = cmd
200 self.env = env
200 self.env = env
201
201
202 def run(self, args):
202 def run(self, args):
203 cmd = self.cmd + args
203 cmd = self.cmd + args
204 returncode, out, err = runcmd(cmd, self.env)
204 returncode, out, err = runcmd(cmd, self.env)
205 err = filterhgerr(err)
205 err = filterhgerr(err)
206 if err or returncode != 0:
206 if err or returncode != 0:
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
207 printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
208 printf(err, file=sys.stderr)
208 printf(err, file=sys.stderr)
209 return ''
209 return ''
210 return out
210 return out
211
211
212 def filterhgerr(err):
212 def filterhgerr(err):
213 # If root is executing setup.py, but the repository is owned by
213 # If root is executing setup.py, but the repository is owned by
214 # another user (as in "sudo python setup.py install") we will get
214 # another user (as in "sudo python setup.py install") we will get
215 # trust warnings since the .hg/hgrc file is untrusted. That is
215 # trust warnings since the .hg/hgrc file is untrusted. That is
216 # fine, we don't want to load it anyway. Python may warn about
216 # fine, we don't want to load it anyway. Python may warn about
217 # a missing __init__.py in mercurial/locale, we also ignore that.
217 # a missing __init__.py in mercurial/locale, we also ignore that.
218 err = [e for e in err.splitlines()
218 err = [e for e in err.splitlines()
219 if (not e.startswith(b'not trusting file')
219 if (not e.startswith(b'not trusting file')
220 and not e.startswith(b'warning: Not importing')
220 and not e.startswith(b'warning: Not importing')
221 and not e.startswith(b'obsolete feature not enabled'))]
221 and not e.startswith(b'obsolete feature not enabled'))]
222 return b'\n'.join(b' ' + e for e in err)
222 return b'\n'.join(b' ' + e for e in err)
223
223
224 def findhg():
224 def findhg():
225 """Try to figure out how we should invoke hg for examining the local
225 """Try to figure out how we should invoke hg for examining the local
226 repository contents.
226 repository contents.
227
227
228 Returns an hgcommand object."""
228 Returns an hgcommand object."""
229 # By default, prefer the "hg" command in the user's path. This was
229 # By default, prefer the "hg" command in the user's path. This was
230 # presumably the hg command that the user used to create this repository.
230 # presumably the hg command that the user used to create this repository.
231 #
231 #
232 # This repository may require extensions or other settings that would not
232 # This repository may require extensions or other settings that would not
233 # be enabled by running the hg script directly from this local repository.
233 # be enabled by running the hg script directly from this local repository.
234 hgenv = os.environ.copy()
234 hgenv = os.environ.copy()
235 # Use HGPLAIN to disable hgrc settings that would change output formatting,
235 # Use HGPLAIN to disable hgrc settings that would change output formatting,
236 # and disable localization for the same reasons.
236 # and disable localization for the same reasons.
237 hgenv['HGPLAIN'] = '1'
237 hgenv['HGPLAIN'] = '1'
238 hgenv['LANGUAGE'] = 'C'
238 hgenv['LANGUAGE'] = 'C'
239 hgcmd = ['hg']
239 hgcmd = ['hg']
240 # Run a simple "hg log" command just to see if using hg from the user's
240 # Run a simple "hg log" command just to see if using hg from the user's
241 # path works and can successfully interact with this repository.
241 # path works and can successfully interact with this repository.
242 check_cmd = ['log', '-r.', '-Ttest']
242 check_cmd = ['log', '-r.', '-Ttest']
243 try:
243 try:
244 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
244 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
245 except EnvironmentError:
245 except EnvironmentError:
246 retcode = -1
246 retcode = -1
247 if retcode == 0 and not filterhgerr(err):
247 if retcode == 0 and not filterhgerr(err):
248 return hgcommand(hgcmd, hgenv)
248 return hgcommand(hgcmd, hgenv)
249
249
250 # Fall back to trying the local hg installation.
250 # Fall back to trying the local hg installation.
251 hgenv = localhgenv()
251 hgenv = localhgenv()
252 # Don't source any system hgrc files when using the local hg.
252 # Don't source any system hgrc files when using the local hg.
253 hgenv['HGRCPATH'] = ''
253 hgenv['HGRCPATH'] = ''
254 hgcmd = [sys.executable, 'hg']
254 hgcmd = [sys.executable, 'hg']
255 try:
255 try:
256 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
256 retcode, out, err = runcmd(hgcmd + check_cmd, hgenv)
257 except EnvironmentError:
257 except EnvironmentError:
258 retcode = -1
258 retcode = -1
259 if retcode == 0 and not filterhgerr(err):
259 if retcode == 0 and not filterhgerr(err):
260 return hgcommand(hgcmd, hgenv)
260 return hgcommand(hgcmd, hgenv)
261
261
262 raise SystemExit('Unable to find a working hg binary to extract the '
262 raise SystemExit('Unable to find a working hg binary to extract the '
263 'version from the repository tags')
263 'version from the repository tags')
264
264
265 def localhgenv():
265 def localhgenv():
266 """Get an environment dictionary to use for invoking or importing
266 """Get an environment dictionary to use for invoking or importing
267 mercurial from the local repository."""
267 mercurial from the local repository."""
268 # Execute hg out of this directory with a custom environment which takes
268 # Execute hg out of this directory with a custom environment which takes
269 # care to not use any hgrc files and do no localization.
269 # care to not use any hgrc files and do no localization.
270 env = {'HGMODULEPOLICY': 'py',
270 env = {'HGMODULEPOLICY': 'py',
271 'HGRCPATH': '',
271 'HGRCPATH': '',
272 'LANGUAGE': 'C',
272 'LANGUAGE': 'C',
273 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
273 'PATH': ''} # make pypi modules that use os.environ['PATH'] happy
274 if 'LD_LIBRARY_PATH' in os.environ:
274 if 'LD_LIBRARY_PATH' in os.environ:
275 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
275 env['LD_LIBRARY_PATH'] = os.environ['LD_LIBRARY_PATH']
276 if 'SystemRoot' in os.environ:
276 if 'SystemRoot' in os.environ:
277 # SystemRoot is required by Windows to load various DLLs. See:
277 # SystemRoot is required by Windows to load various DLLs. See:
278 # https://bugs.python.org/issue13524#msg148850
278 # https://bugs.python.org/issue13524#msg148850
279 env['SystemRoot'] = os.environ['SystemRoot']
279 env['SystemRoot'] = os.environ['SystemRoot']
280 return env
280 return env
281
281
282 version = ''
282 version = ''
283
283
284 if os.path.isdir('.hg'):
284 if os.path.isdir('.hg'):
285 hg = findhg()
285 hg = findhg()
286 cmd = ['log', '-r', '.', '--template', '{tags}\n']
286 cmd = ['log', '-r', '.', '--template', '{tags}\n']
287 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
287 numerictags = [t for t in hg.run(cmd).split() if t[0:1].isdigit()]
288 hgid = hg.run(['id', '-i']).strip()
288 hgid = hg.run(['id', '-i']).strip()
289 if not hgid:
289 if not hgid:
290 # Bail out if hg is having problems interacting with this repository,
290 # Bail out if hg is having problems interacting with this repository,
291 # rather than falling through and producing a bogus version number.
291 # rather than falling through and producing a bogus version number.
292 # Continuing with an invalid version number will break extensions
292 # Continuing with an invalid version number will break extensions
293 # that define minimumhgversion.
293 # that define minimumhgversion.
294 raise SystemExit('Unable to determine hg version from local repository')
294 raise SystemExit('Unable to determine hg version from local repository')
295 if numerictags: # tag(s) found
295 if numerictags: # tag(s) found
296 version = numerictags[-1]
296 version = numerictags[-1]
297 if hgid.endswith('+'): # propagate the dirty status to the tag
297 if hgid.endswith('+'): # propagate the dirty status to the tag
298 version += '+'
298 version += '+'
299 else: # no tag found
299 else: # no tag found
300 ltagcmd = ['parents', '--template', '{latesttag}']
300 ltagcmd = ['parents', '--template', '{latesttag}']
301 ltag = hg.run(ltagcmd)
301 ltag = hg.run(ltagcmd)
302 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
302 changessincecmd = ['log', '-T', 'x\n', '-r', "only(.,'%s')" % ltag]
303 changessince = len(hg.run(changessincecmd).splitlines())
303 changessince = len(hg.run(changessincecmd).splitlines())
304 version = '%s+%s-%s' % (ltag, changessince, hgid)
304 version = '%s+%s-%s' % (ltag, changessince, hgid)
305 if version.endswith('+'):
305 if version.endswith('+'):
306 version += time.strftime('%Y%m%d')
306 version += time.strftime('%Y%m%d')
307 elif os.path.exists('.hg_archival.txt'):
307 elif os.path.exists('.hg_archival.txt'):
308 kw = dict([[t.strip() for t in l.split(':', 1)]
308 kw = dict([[t.strip() for t in l.split(':', 1)]
309 for l in open('.hg_archival.txt')])
309 for l in open('.hg_archival.txt')])
310 if 'tag' in kw:
310 if 'tag' in kw:
311 version = kw['tag']
311 version = kw['tag']
312 elif 'latesttag' in kw:
312 elif 'latesttag' in kw:
313 if 'changessincelatesttag' in kw:
313 if 'changessincelatesttag' in kw:
314 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
314 version = '%(latesttag)s+%(changessincelatesttag)s-%(node).12s' % kw
315 else:
315 else:
316 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
316 version = '%(latesttag)s+%(latesttagdistance)s-%(node).12s' % kw
317 else:
317 else:
318 version = kw.get('node', '')[:12]
318 version = kw.get('node', '')[:12]
319
319
320 if version:
320 if version:
321 with open("mercurial/__version__.py", "w") as f:
321 with open("mercurial/__version__.py", "w") as f:
322 f.write('# this file is autogenerated by setup.py\n')
322 f.write('# this file is autogenerated by setup.py\n')
323 f.write('version = "%s"\n' % version)
323 f.write('version = "%s"\n' % version)
324
324
325 try:
325 try:
326 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
326 oldpolicy = os.environ.get('HGMODULEPOLICY', None)
327 os.environ['HGMODULEPOLICY'] = 'py'
327 os.environ['HGMODULEPOLICY'] = 'py'
328 from mercurial import __version__
328 from mercurial import __version__
329 version = __version__.version
329 version = __version__.version
330 except ImportError:
330 except ImportError:
331 version = 'unknown'
331 version = 'unknown'
332 finally:
332 finally:
333 if oldpolicy is None:
333 if oldpolicy is None:
334 del os.environ['HGMODULEPOLICY']
334 del os.environ['HGMODULEPOLICY']
335 else:
335 else:
336 os.environ['HGMODULEPOLICY'] = oldpolicy
336 os.environ['HGMODULEPOLICY'] = oldpolicy
337
337
338 class hgbuild(build):
338 class hgbuild(build):
339 # Insert hgbuildmo first so that files in mercurial/locale/ are found
339 # Insert hgbuildmo first so that files in mercurial/locale/ are found
340 # when build_py is run next.
340 # when build_py is run next.
341 sub_commands = [('build_mo', None)] + build.sub_commands
341 sub_commands = [('build_mo', None)] + build.sub_commands
342
342
343 class hgbuildmo(build):
343 class hgbuildmo(build):
344
344
345 description = "build translations (.mo files)"
345 description = "build translations (.mo files)"
346
346
347 def run(self):
347 def run(self):
348 if not find_executable('msgfmt'):
348 if not find_executable('msgfmt'):
349 self.warn("could not find msgfmt executable, no translations "
349 self.warn("could not find msgfmt executable, no translations "
350 "will be built")
350 "will be built")
351 return
351 return
352
352
353 podir = 'i18n'
353 podir = 'i18n'
354 if not os.path.isdir(podir):
354 if not os.path.isdir(podir):
355 self.warn("could not find %s/ directory" % podir)
355 self.warn("could not find %s/ directory" % podir)
356 return
356 return
357
357
358 join = os.path.join
358 join = os.path.join
359 for po in os.listdir(podir):
359 for po in os.listdir(podir):
360 if not po.endswith('.po'):
360 if not po.endswith('.po'):
361 continue
361 continue
362 pofile = join(podir, po)
362 pofile = join(podir, po)
363 modir = join('locale', po[:-3], 'LC_MESSAGES')
363 modir = join('locale', po[:-3], 'LC_MESSAGES')
364 mofile = join(modir, 'hg.mo')
364 mofile = join(modir, 'hg.mo')
365 mobuildfile = join('mercurial', mofile)
365 mobuildfile = join('mercurial', mofile)
366 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
366 cmd = ['msgfmt', '-v', '-o', mobuildfile, pofile]
367 if sys.platform != 'sunos5':
367 if sys.platform != 'sunos5':
368 # msgfmt on Solaris does not know about -c
368 # msgfmt on Solaris does not know about -c
369 cmd.append('-c')
369 cmd.append('-c')
370 self.mkpath(join('mercurial', modir))
370 self.mkpath(join('mercurial', modir))
371 self.make_file([pofile], mobuildfile, spawn, (cmd,))
371 self.make_file([pofile], mobuildfile, spawn, (cmd,))
372
372
373
373
374 class hgdist(Distribution):
374 class hgdist(Distribution):
375 pure = False
375 pure = False
376 cffi = ispypy
376 cffi = ispypy
377
377
378 global_options = Distribution.global_options + \
378 global_options = Distribution.global_options + \
379 [('pure', None, "use pure (slow) Python "
379 [('pure', None, "use pure (slow) Python "
380 "code instead of C extensions"),
380 "code instead of C extensions"),
381 ]
381 ]
382
382
383 def has_ext_modules(self):
383 def has_ext_modules(self):
384 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
384 # self.ext_modules is emptied in hgbuildpy.finalize_options which is
385 # too late for some cases
385 # too late for some cases
386 return not self.pure and Distribution.has_ext_modules(self)
386 return not self.pure and Distribution.has_ext_modules(self)
387
387
388 # This is ugly as a one-liner. So use a variable.
388 # This is ugly as a one-liner. So use a variable.
389 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
389 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
390 buildextnegops['no-zstd'] = 'zstd'
390 buildextnegops['no-zstd'] = 'zstd'
391
391
392 class hgbuildext(build_ext):
392 class hgbuildext(build_ext):
393 user_options = build_ext.user_options + [
393 user_options = build_ext.user_options + [
394 ('zstd', None, 'compile zstd bindings [default]'),
394 ('zstd', None, 'compile zstd bindings [default]'),
395 ('no-zstd', None, 'do not compile zstd bindings'),
395 ('no-zstd', None, 'do not compile zstd bindings'),
396 ]
396 ]
397
397
398 boolean_options = build_ext.boolean_options + ['zstd']
398 boolean_options = build_ext.boolean_options + ['zstd']
399 negative_opt = buildextnegops
399 negative_opt = buildextnegops
400
400
401 def initialize_options(self):
401 def initialize_options(self):
402 self.zstd = True
402 self.zstd = True
403 return build_ext.initialize_options(self)
403 return build_ext.initialize_options(self)
404
404
405 def build_extensions(self):
405 def build_extensions(self):
406 # Filter out zstd if disabled via argument.
406 # Filter out zstd if disabled via argument.
407 if not self.zstd:
407 if not self.zstd:
408 self.extensions = [e for e in self.extensions
408 self.extensions = [e for e in self.extensions
409 if e.name != 'mercurial.zstd']
409 if e.name != 'mercurial.zstd']
410
410
411 return build_ext.build_extensions(self)
411 return build_ext.build_extensions(self)
412
412
413 def build_extension(self, ext):
413 def build_extension(self, ext):
414 try:
414 try:
415 build_ext.build_extension(self, ext)
415 build_ext.build_extension(self, ext)
416 except CCompilerError:
416 except CCompilerError:
417 if not getattr(ext, 'optional', False):
417 if not getattr(ext, 'optional', False):
418 raise
418 raise
419 log.warn("Failed to build optional extension '%s' (skipping)",
419 log.warn("Failed to build optional extension '%s' (skipping)",
420 ext.name)
420 ext.name)
421
421
422 class hgbuildscripts(build_scripts):
422 class hgbuildscripts(build_scripts):
423 def run(self):
423 def run(self):
424 if os.name != 'nt' or self.distribution.pure:
424 if os.name != 'nt' or self.distribution.pure:
425 return build_scripts.run(self)
425 return build_scripts.run(self)
426
426
427 exebuilt = False
427 exebuilt = False
428 try:
428 try:
429 self.run_command('build_hgexe')
429 self.run_command('build_hgexe')
430 exebuilt = True
430 exebuilt = True
431 except (DistutilsError, CCompilerError):
431 except (DistutilsError, CCompilerError):
432 log.warn('failed to build optional hg.exe')
432 log.warn('failed to build optional hg.exe')
433
433
434 if exebuilt:
434 if exebuilt:
435 # Copying hg.exe to the scripts build directory ensures it is
435 # Copying hg.exe to the scripts build directory ensures it is
436 # installed by the install_scripts command.
436 # installed by the install_scripts command.
437 hgexecommand = self.get_finalized_command('build_hgexe')
437 hgexecommand = self.get_finalized_command('build_hgexe')
438 dest = os.path.join(self.build_dir, 'hg.exe')
438 dest = os.path.join(self.build_dir, 'hg.exe')
439 self.mkpath(self.build_dir)
439 self.mkpath(self.build_dir)
440 self.copy_file(hgexecommand.hgexepath, dest)
440 self.copy_file(hgexecommand.hgexepath, dest)
441
441
442 # Remove hg.bat because it is redundant with hg.exe.
442 # Remove hg.bat because it is redundant with hg.exe.
443 self.scripts.remove('contrib/win32/hg.bat')
443 self.scripts.remove('contrib/win32/hg.bat')
444
444
445 return build_scripts.run(self)
445 return build_scripts.run(self)
446
446
447 class hgbuildpy(build_py):
447 class hgbuildpy(build_py):
448 def finalize_options(self):
448 def finalize_options(self):
449 build_py.finalize_options(self)
449 build_py.finalize_options(self)
450
450
451 if self.distribution.pure:
451 if self.distribution.pure:
452 self.distribution.ext_modules = []
452 self.distribution.ext_modules = []
453 elif self.distribution.cffi:
453 elif self.distribution.cffi:
454 from mercurial.cffi import (
454 from mercurial.cffi import (
455 bdiffbuild,
455 bdiffbuild,
456 mpatchbuild,
456 mpatchbuild,
457 )
457 )
458 exts = [mpatchbuild.ffi.distutils_extension(),
458 exts = [mpatchbuild.ffi.distutils_extension(),
459 bdiffbuild.ffi.distutils_extension()]
459 bdiffbuild.ffi.distutils_extension()]
460 # cffi modules go here
460 # cffi modules go here
461 if sys.platform == 'darwin':
461 if sys.platform == 'darwin':
462 from mercurial.cffi import osutilbuild
462 from mercurial.cffi import osutilbuild
463 exts.append(osutilbuild.ffi.distutils_extension())
463 exts.append(osutilbuild.ffi.distutils_extension())
464 self.distribution.ext_modules = exts
464 self.distribution.ext_modules = exts
465 else:
465 else:
466 h = os.path.join(get_python_inc(), 'Python.h')
466 h = os.path.join(get_python_inc(), 'Python.h')
467 if not os.path.exists(h):
467 if not os.path.exists(h):
468 raise SystemExit('Python headers are required to build '
468 raise SystemExit('Python headers are required to build '
469 'Mercurial but weren\'t found in %s' % h)
469 'Mercurial but weren\'t found in %s' % h)
470
470
471 def run(self):
471 def run(self):
472 basepath = os.path.join(self.build_lib, 'mercurial')
472 basepath = os.path.join(self.build_lib, 'mercurial')
473 self.mkpath(basepath)
473 self.mkpath(basepath)
474
474
475 if self.distribution.pure:
475 if self.distribution.pure:
476 modulepolicy = 'py'
476 modulepolicy = 'py'
477 elif self.build_lib == '.':
477 elif self.build_lib == '.':
478 # in-place build should run without rebuilding C extensions
478 # in-place build should run without rebuilding C extensions
479 modulepolicy = 'allow'
479 modulepolicy = 'allow'
480 else:
480 else:
481 modulepolicy = 'c'
481 modulepolicy = 'c'
482 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
482 with open(os.path.join(basepath, '__modulepolicy__.py'), "w") as f:
483 f.write('# this file is autogenerated by setup.py\n')
483 f.write('# this file is autogenerated by setup.py\n')
484 f.write('modulepolicy = b"%s"\n' % modulepolicy)
484 f.write('modulepolicy = b"%s"\n' % modulepolicy)
485
485
486 build_py.run(self)
486 build_py.run(self)
487
487
488 class buildhgextindex(Command):
488 class buildhgextindex(Command):
489 description = 'generate prebuilt index of hgext (for frozen package)'
489 description = 'generate prebuilt index of hgext (for frozen package)'
490 user_options = []
490 user_options = []
491 _indexfilename = 'hgext/__index__.py'
491 _indexfilename = 'hgext/__index__.py'
492
492
493 def initialize_options(self):
493 def initialize_options(self):
494 pass
494 pass
495
495
496 def finalize_options(self):
496 def finalize_options(self):
497 pass
497 pass
498
498
499 def run(self):
499 def run(self):
500 if os.path.exists(self._indexfilename):
500 if os.path.exists(self._indexfilename):
501 with open(self._indexfilename, 'w') as f:
501 with open(self._indexfilename, 'w') as f:
502 f.write('# empty\n')
502 f.write('# empty\n')
503
503
504 # here no extension enabled, disabled() lists up everything
504 # here no extension enabled, disabled() lists up everything
505 code = ('import pprint; from mercurial import extensions; '
505 code = ('import pprint; from mercurial import extensions; '
506 'pprint.pprint(extensions.disabled())')
506 'pprint.pprint(extensions.disabled())')
507 returncode, out, err = runcmd([sys.executable, '-c', code],
507 returncode, out, err = runcmd([sys.executable, '-c', code],
508 localhgenv())
508 localhgenv())
509 if err or returncode != 0:
509 if err or returncode != 0:
510 raise DistutilsExecError(err)
510 raise DistutilsExecError(err)
511
511
512 with open(self._indexfilename, 'w') as f:
512 with open(self._indexfilename, 'w') as f:
513 f.write('# this file is autogenerated by setup.py\n')
513 f.write('# this file is autogenerated by setup.py\n')
514 f.write('docs = ')
514 f.write('docs = ')
515 f.write(out)
515 f.write(out)
516
516
517 class buildhgexe(build_ext):
517 class buildhgexe(build_ext):
518 description = 'compile hg.exe from mercurial/exewrapper.c'
518 description = 'compile hg.exe from mercurial/exewrapper.c'
519
519
520 def build_extensions(self):
520 def build_extensions(self):
521 if os.name != 'nt':
521 if os.name != 'nt':
522 return
522 return
523 if isinstance(self.compiler, HackedMingw32CCompiler):
523 if isinstance(self.compiler, HackedMingw32CCompiler):
524 self.compiler.compiler_so = self.compiler.compiler # no -mdll
524 self.compiler.compiler_so = self.compiler.compiler # no -mdll
525 self.compiler.dll_libraries = [] # no -lmsrvc90
525 self.compiler.dll_libraries = [] # no -lmsrvc90
526
526
527 # Different Python installs can have different Python library
527 # Different Python installs can have different Python library
528 # names. e.g. the official CPython distribution uses pythonXY.dll
528 # names. e.g. the official CPython distribution uses pythonXY.dll
529 # and MinGW uses libpythonX.Y.dll.
529 # and MinGW uses libpythonX.Y.dll.
530 _kernel32 = ctypes.windll.kernel32
530 _kernel32 = ctypes.windll.kernel32
531 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
531 _kernel32.GetModuleFileNameA.argtypes = [ctypes.c_void_p,
532 ctypes.c_void_p,
532 ctypes.c_void_p,
533 ctypes.c_ulong]
533 ctypes.c_ulong]
534 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
534 _kernel32.GetModuleFileNameA.restype = ctypes.c_ulong
535 size = 1000
535 size = 1000
536 buf = ctypes.create_string_buffer(size + 1)
536 buf = ctypes.create_string_buffer(size + 1)
537 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
537 filelen = _kernel32.GetModuleFileNameA(sys.dllhandle, ctypes.byref(buf),
538 size)
538 size)
539
539
540 if filelen > 0 and filelen != size:
540 if filelen > 0 and filelen != size:
541 dllbasename = os.path.basename(buf.value)
541 dllbasename = os.path.basename(buf.value)
542 if not dllbasename.lower().endswith('.dll'):
542 if not dllbasename.lower().endswith('.dll'):
543 raise SystemExit('Python DLL does not end with .dll: %s' %
543 raise SystemExit('Python DLL does not end with .dll: %s' %
544 dllbasename)
544 dllbasename)
545 pythonlib = dllbasename[:-4]
545 pythonlib = dllbasename[:-4]
546 else:
546 else:
547 log.warn('could not determine Python DLL filename; '
547 log.warn('could not determine Python DLL filename; '
548 'assuming pythonXY')
548 'assuming pythonXY')
549
549
550 hv = sys.hexversion
550 hv = sys.hexversion
551 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
551 pythonlib = 'python%d%d' % (hv >> 24, (hv >> 16) & 0xff)
552
552
553 log.info('using %s as Python library name' % pythonlib)
553 log.info('using %s as Python library name' % pythonlib)
554 with open('mercurial/hgpythonlib.h', 'wb') as f:
554 with open('mercurial/hgpythonlib.h', 'wb') as f:
555 f.write('/* this file is autogenerated by setup.py */\n')
555 f.write('/* this file is autogenerated by setup.py */\n')
556 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
556 f.write('#define HGPYTHONLIB "%s"\n' % pythonlib)
557 objects = self.compiler.compile(['mercurial/exewrapper.c'],
557 objects = self.compiler.compile(['mercurial/exewrapper.c'],
558 output_dir=self.build_temp)
558 output_dir=self.build_temp)
559 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
559 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
560 target = os.path.join(dir, 'hg')
560 target = os.path.join(dir, 'hg')
561 self.compiler.link_executable(objects, target,
561 self.compiler.link_executable(objects, target,
562 libraries=[],
562 libraries=[],
563 output_dir=self.build_temp)
563 output_dir=self.build_temp)
564
564
565 @property
565 @property
566 def hgexepath(self):
566 def hgexepath(self):
567 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
567 dir = os.path.dirname(self.get_ext_fullpath('dummy'))
568 return os.path.join(self.build_temp, dir, 'hg.exe')
568 return os.path.join(self.build_temp, dir, 'hg.exe')
569
569
570 class hginstall(install):
570 class hginstall(install):
571
571
572 user_options = install.user_options + [
572 user_options = install.user_options + [
573 ('old-and-unmanageable', None,
573 ('old-and-unmanageable', None,
574 'noop, present for eggless setuptools compat'),
574 'noop, present for eggless setuptools compat'),
575 ('single-version-externally-managed', None,
575 ('single-version-externally-managed', None,
576 'noop, present for eggless setuptools compat'),
576 'noop, present for eggless setuptools compat'),
577 ]
577 ]
578
578
579 # Also helps setuptools not be sad while we refuse to create eggs.
579 # Also helps setuptools not be sad while we refuse to create eggs.
580 single_version_externally_managed = True
580 single_version_externally_managed = True
581
581
582 def get_sub_commands(self):
582 def get_sub_commands(self):
583 # Screen out egg related commands to prevent egg generation. But allow
583 # Screen out egg related commands to prevent egg generation. But allow
584 # mercurial.egg-info generation, since that is part of modern
584 # mercurial.egg-info generation, since that is part of modern
585 # packaging.
585 # packaging.
586 excl = set(['bdist_egg'])
586 excl = set(['bdist_egg'])
587 return filter(lambda x: x not in excl, install.get_sub_commands(self))
587 return filter(lambda x: x not in excl, install.get_sub_commands(self))
588
588
589 class hginstalllib(install_lib):
589 class hginstalllib(install_lib):
590 '''
590 '''
591 This is a specialization of install_lib that replaces the copy_file used
591 This is a specialization of install_lib that replaces the copy_file used
592 there so that it supports setting the mode of files after copying them,
592 there so that it supports setting the mode of files after copying them,
593 instead of just preserving the mode that the files originally had. If your
593 instead of just preserving the mode that the files originally had. If your
594 system has a umask of something like 027, preserving the permissions when
594 system has a umask of something like 027, preserving the permissions when
595 copying will lead to a broken install.
595 copying will lead to a broken install.
596
596
597 Note that just passing keep_permissions=False to copy_file would be
597 Note that just passing keep_permissions=False to copy_file would be
598 insufficient, as it might still be applying a umask.
598 insufficient, as it might still be applying a umask.
599 '''
599 '''
600
600
601 def run(self):
601 def run(self):
602 realcopyfile = file_util.copy_file
602 realcopyfile = file_util.copy_file
603 def copyfileandsetmode(*args, **kwargs):
603 def copyfileandsetmode(*args, **kwargs):
604 src, dst = args[0], args[1]
604 src, dst = args[0], args[1]
605 dst, copied = realcopyfile(*args, **kwargs)
605 dst, copied = realcopyfile(*args, **kwargs)
606 if copied:
606 if copied:
607 st = os.stat(src)
607 st = os.stat(src)
608 # Persist executable bit (apply it to group and other if user
608 # Persist executable bit (apply it to group and other if user
609 # has it)
609 # has it)
610 if st[stat.ST_MODE] & stat.S_IXUSR:
610 if st[stat.ST_MODE] & stat.S_IXUSR:
611 setmode = int('0755', 8)
611 setmode = int('0755', 8)
612 else:
612 else:
613 setmode = int('0644', 8)
613 setmode = int('0644', 8)
614 m = stat.S_IMODE(st[stat.ST_MODE])
614 m = stat.S_IMODE(st[stat.ST_MODE])
615 m = (m & ~int('0777', 8)) | setmode
615 m = (m & ~int('0777', 8)) | setmode
616 os.chmod(dst, m)
616 os.chmod(dst, m)
617 file_util.copy_file = copyfileandsetmode
617 file_util.copy_file = copyfileandsetmode
618 try:
618 try:
619 install_lib.run(self)
619 install_lib.run(self)
620 finally:
620 finally:
621 file_util.copy_file = realcopyfile
621 file_util.copy_file = realcopyfile
622
622
623 class hginstallscripts(install_scripts):
623 class hginstallscripts(install_scripts):
624 '''
624 '''
625 This is a specialization of install_scripts that replaces the @LIBDIR@ with
625 This is a specialization of install_scripts that replaces the @LIBDIR@ with
626 the configured directory for modules. If possible, the path is made relative
626 the configured directory for modules. If possible, the path is made relative
627 to the directory for scripts.
627 to the directory for scripts.
628 '''
628 '''
629
629
630 def initialize_options(self):
630 def initialize_options(self):
631 install_scripts.initialize_options(self)
631 install_scripts.initialize_options(self)
632
632
633 self.install_lib = None
633 self.install_lib = None
634
634
635 def finalize_options(self):
635 def finalize_options(self):
636 install_scripts.finalize_options(self)
636 install_scripts.finalize_options(self)
637 self.set_undefined_options('install',
637 self.set_undefined_options('install',
638 ('install_lib', 'install_lib'))
638 ('install_lib', 'install_lib'))
639
639
640 def run(self):
640 def run(self):
641 install_scripts.run(self)
641 install_scripts.run(self)
642
642
643 # It only makes sense to replace @LIBDIR@ with the install path if
643 # It only makes sense to replace @LIBDIR@ with the install path if
644 # the install path is known. For wheels, the logic below calculates
644 # the install path is known. For wheels, the logic below calculates
645 # the libdir to be "../..". This is because the internal layout of a
645 # the libdir to be "../..". This is because the internal layout of a
646 # wheel archive looks like:
646 # wheel archive looks like:
647 #
647 #
648 # mercurial-3.6.1.data/scripts/hg
648 # mercurial-3.6.1.data/scripts/hg
649 # mercurial/__init__.py
649 # mercurial/__init__.py
650 #
650 #
651 # When installing wheels, the subdirectories of the "<pkg>.data"
651 # When installing wheels, the subdirectories of the "<pkg>.data"
652 # directory are translated to system local paths and files therein
652 # directory are translated to system local paths and files therein
653 # are copied in place. The mercurial/* files are installed into the
653 # are copied in place. The mercurial/* files are installed into the
654 # site-packages directory. However, the site-packages directory
654 # site-packages directory. However, the site-packages directory
655 # isn't known until wheel install time. This means we have no clue
655 # isn't known until wheel install time. This means we have no clue
656 # at wheel generation time what the installed site-packages directory
656 # at wheel generation time what the installed site-packages directory
657 # will be. And, wheels don't appear to provide the ability to register
657 # will be. And, wheels don't appear to provide the ability to register
658 # custom code to run during wheel installation. This all means that
658 # custom code to run during wheel installation. This all means that
659 # we can't reliably set the libdir in wheels: the default behavior
659 # we can't reliably set the libdir in wheels: the default behavior
660 # of looking in sys.path must do.
660 # of looking in sys.path must do.
661
661
662 if (os.path.splitdrive(self.install_dir)[0] !=
662 if (os.path.splitdrive(self.install_dir)[0] !=
663 os.path.splitdrive(self.install_lib)[0]):
663 os.path.splitdrive(self.install_lib)[0]):
664 # can't make relative paths from one drive to another, so use an
664 # can't make relative paths from one drive to another, so use an
665 # absolute path instead
665 # absolute path instead
666 libdir = self.install_lib
666 libdir = self.install_lib
667 else:
667 else:
668 common = os.path.commonprefix((self.install_dir, self.install_lib))
668 common = os.path.commonprefix((self.install_dir, self.install_lib))
669 rest = self.install_dir[len(common):]
669 rest = self.install_dir[len(common):]
670 uplevel = len([n for n in os.path.split(rest) if n])
670 uplevel = len([n for n in os.path.split(rest) if n])
671
671
672 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
672 libdir = uplevel * ('..' + os.sep) + self.install_lib[len(common):]
673
673
674 for outfile in self.outfiles:
674 for outfile in self.outfiles:
675 with open(outfile, 'rb') as fp:
675 with open(outfile, 'rb') as fp:
676 data = fp.read()
676 data = fp.read()
677
677
678 # skip binary files
678 # skip binary files
679 if b'\0' in data:
679 if b'\0' in data:
680 continue
680 continue
681
681
682 # During local installs, the shebang will be rewritten to the final
682 # During local installs, the shebang will be rewritten to the final
683 # install path. During wheel packaging, the shebang has a special
683 # install path. During wheel packaging, the shebang has a special
684 # value.
684 # value.
685 if data.startswith(b'#!python'):
685 if data.startswith(b'#!python'):
686 log.info('not rewriting @LIBDIR@ in %s because install path '
686 log.info('not rewriting @LIBDIR@ in %s because install path '
687 'not known' % outfile)
687 'not known' % outfile)
688 continue
688 continue
689
689
690 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
690 data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
691 with open(outfile, 'wb') as fp:
691 with open(outfile, 'wb') as fp:
692 fp.write(data)
692 fp.write(data)
693
693
694 cmdclass = {'build': hgbuild,
694 cmdclass = {'build': hgbuild,
695 'build_mo': hgbuildmo,
695 'build_mo': hgbuildmo,
696 'build_ext': hgbuildext,
696 'build_ext': hgbuildext,
697 'build_py': hgbuildpy,
697 'build_py': hgbuildpy,
698 'build_scripts': hgbuildscripts,
698 'build_scripts': hgbuildscripts,
699 'build_hgextindex': buildhgextindex,
699 'build_hgextindex': buildhgextindex,
700 'install': hginstall,
700 'install': hginstall,
701 'install_lib': hginstalllib,
701 'install_lib': hginstalllib,
702 'install_scripts': hginstallscripts,
702 'install_scripts': hginstallscripts,
703 'build_hgexe': buildhgexe,
703 'build_hgexe': buildhgexe,
704 }
704 }
705
705
706 packages = ['mercurial',
706 packages = ['mercurial',
707 'mercurial.cext',
707 'mercurial.cext',
708 'mercurial.cffi',
708 'mercurial.cffi',
709 'mercurial.hgweb',
709 'mercurial.hgweb',
710 'mercurial.httpclient',
710 'mercurial.httpclient',
711 'mercurial.pure',
711 'mercurial.pure',
712 'hgext', 'hgext.convert', 'hgext.fsmonitor',
712 'hgext', 'hgext.convert', 'hgext.fsmonitor',
713 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
713 'hgext.fsmonitor.pywatchman', 'hgext.highlight',
714 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
714 'hgext.largefiles', 'hgext.zeroconf', 'hgext3rd',
715 'hgdemandimport']
715 'hgdemandimport']
716
716
717 common_depends = ['mercurial/bitmanipulation.h',
717 common_depends = ['mercurial/bitmanipulation.h',
718 'mercurial/compat.h',
718 'mercurial/compat.h',
719 'mercurial/cext/util.h']
719 'mercurial/cext/util.h']
720 common_include_dirs = ['mercurial']
720 common_include_dirs = ['mercurial']
721
721
722 osutil_cflags = []
722 osutil_cflags = []
723 osutil_ldflags = []
723 osutil_ldflags = []
724
724
725 # platform specific macros
725 # platform specific macros
726 for plat, func in [('bsd', 'setproctitle')]:
726 for plat, func in [('bsd', 'setproctitle')]:
727 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
727 if re.search(plat, sys.platform) and hasfunction(new_compiler(), func):
728 osutil_cflags.append('-DHAVE_%s' % func.upper())
728 osutil_cflags.append('-DHAVE_%s' % func.upper())
729
729
730 for plat, macro, code in [
730 for plat, macro, code in [
731 ('bsd|darwin', 'BSD_STATFS', '''
731 ('bsd|darwin', 'BSD_STATFS', '''
732 #include <sys/param.h>
732 #include <sys/param.h>
733 #include <sys/mount.h>
733 #include <sys/mount.h>
734 int main() { struct statfs s; return sizeof(s.f_fstypename); }
734 int main() { struct statfs s; return sizeof(s.f_fstypename); }
735 '''),
735 '''),
736 ('linux', 'LINUX_STATFS', '''
736 ('linux', 'LINUX_STATFS', '''
737 #include <linux/magic.h>
737 #include <linux/magic.h>
738 #include <sys/vfs.h>
738 #include <sys/vfs.h>
739 int main() { struct statfs s; return sizeof(s.f_type); }
739 int main() { struct statfs s; return sizeof(s.f_type); }
740 '''),
740 '''),
741 ]:
741 ]:
742 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
742 if re.search(plat, sys.platform) and cancompile(new_compiler(), code):
743 osutil_cflags.append('-DHAVE_%s' % macro)
743 osutil_cflags.append('-DHAVE_%s' % macro)
744
744
745 if sys.platform == 'darwin':
745 if sys.platform == 'darwin':
746 osutil_ldflags += ['-framework', 'ApplicationServices']
746 osutil_ldflags += ['-framework', 'ApplicationServices']
747
747
748 extmodules = [
748 extmodules = [
749 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
749 Extension('mercurial.cext.base85', ['mercurial/cext/base85.c'],
750 include_dirs=common_include_dirs,
750 include_dirs=common_include_dirs,
751 depends=common_depends),
751 depends=common_depends),
752 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
752 Extension('mercurial.cext.bdiff', ['mercurial/bdiff.c',
753 'mercurial/cext/bdiff.c'],
753 'mercurial/cext/bdiff.c'],
754 include_dirs=common_include_dirs,
754 include_dirs=common_include_dirs,
755 depends=common_depends + ['mercurial/bdiff.h']),
755 depends=common_depends + ['mercurial/bdiff.h']),
756 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
756 Extension('mercurial.cext.diffhelpers', ['mercurial/cext/diffhelpers.c'],
757 include_dirs=common_include_dirs,
757 include_dirs=common_include_dirs,
758 depends=common_depends),
758 depends=common_depends),
759 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
759 Extension('mercurial.cext.mpatch', ['mercurial/mpatch.c',
760 'mercurial/cext/mpatch.c'],
760 'mercurial/cext/mpatch.c'],
761 include_dirs=common_include_dirs,
761 include_dirs=common_include_dirs,
762 depends=common_depends),
762 depends=common_depends),
763 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
763 Extension('mercurial.cext.parsers', ['mercurial/cext/charencode.c',
764 'mercurial/cext/dirs.c',
764 'mercurial/cext/dirs.c',
765 'mercurial/cext/manifest.c',
765 'mercurial/cext/manifest.c',
766 'mercurial/cext/parsers.c',
766 'mercurial/cext/parsers.c',
767 'mercurial/cext/pathencode.c',
767 'mercurial/cext/pathencode.c',
768 'mercurial/cext/revlog.c'],
768 'mercurial/cext/revlog.c'],
769 include_dirs=common_include_dirs,
769 include_dirs=common_include_dirs,
770 depends=common_depends),
770 depends=common_depends + ['mercurial/cext/charencode.h']),
771 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
771 Extension('mercurial.cext.osutil', ['mercurial/cext/osutil.c'],
772 include_dirs=common_include_dirs,
772 include_dirs=common_include_dirs,
773 extra_compile_args=osutil_cflags,
773 extra_compile_args=osutil_cflags,
774 extra_link_args=osutil_ldflags,
774 extra_link_args=osutil_ldflags,
775 depends=common_depends),
775 depends=common_depends),
776 Extension('hgext.fsmonitor.pywatchman.bser',
776 Extension('hgext.fsmonitor.pywatchman.bser',
777 ['hgext/fsmonitor/pywatchman/bser.c']),
777 ['hgext/fsmonitor/pywatchman/bser.c']),
778 ]
778 ]
779
779
780 sys.path.insert(0, 'contrib/python-zstandard')
780 sys.path.insert(0, 'contrib/python-zstandard')
781 import setup_zstd
781 import setup_zstd
782 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
782 extmodules.append(setup_zstd.get_c_extension(name='mercurial.zstd'))
783
783
784 try:
784 try:
785 from distutils import cygwinccompiler
785 from distutils import cygwinccompiler
786
786
787 # the -mno-cygwin option has been deprecated for years
787 # the -mno-cygwin option has been deprecated for years
788 compiler = cygwinccompiler.Mingw32CCompiler
788 compiler = cygwinccompiler.Mingw32CCompiler
789
789
790 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
790 class HackedMingw32CCompiler(cygwinccompiler.Mingw32CCompiler):
791 def __init__(self, *args, **kwargs):
791 def __init__(self, *args, **kwargs):
792 compiler.__init__(self, *args, **kwargs)
792 compiler.__init__(self, *args, **kwargs)
793 for i in 'compiler compiler_so linker_exe linker_so'.split():
793 for i in 'compiler compiler_so linker_exe linker_so'.split():
794 try:
794 try:
795 getattr(self, i).remove('-mno-cygwin')
795 getattr(self, i).remove('-mno-cygwin')
796 except ValueError:
796 except ValueError:
797 pass
797 pass
798
798
799 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
799 cygwinccompiler.Mingw32CCompiler = HackedMingw32CCompiler
800 except ImportError:
800 except ImportError:
801 # the cygwinccompiler package is not available on some Python
801 # the cygwinccompiler package is not available on some Python
802 # distributions like the ones from the optware project for Synology
802 # distributions like the ones from the optware project for Synology
803 # DiskStation boxes
803 # DiskStation boxes
804 class HackedMingw32CCompiler(object):
804 class HackedMingw32CCompiler(object):
805 pass
805 pass
806
806
807 if os.name == 'nt':
807 if os.name == 'nt':
808 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
808 # Allow compiler/linker flags to be added to Visual Studio builds. Passing
809 # extra_link_args to distutils.extensions.Extension() doesn't have any
809 # extra_link_args to distutils.extensions.Extension() doesn't have any
810 # effect.
810 # effect.
811 from distutils import msvccompiler
811 from distutils import msvccompiler
812
812
813 compiler = msvccompiler.MSVCCompiler
813 compiler = msvccompiler.MSVCCompiler
814
814
815 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
815 class HackedMSVCCompiler(msvccompiler.MSVCCompiler):
816 def initialize(self):
816 def initialize(self):
817 compiler.initialize(self)
817 compiler.initialize(self)
818 # "warning LNK4197: export 'func' specified multiple times"
818 # "warning LNK4197: export 'func' specified multiple times"
819 self.ldflags_shared.append('/ignore:4197')
819 self.ldflags_shared.append('/ignore:4197')
820 self.ldflags_shared_debug.append('/ignore:4197')
820 self.ldflags_shared_debug.append('/ignore:4197')
821
821
822 msvccompiler.MSVCCompiler = HackedMSVCCompiler
822 msvccompiler.MSVCCompiler = HackedMSVCCompiler
823
823
824 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
824 packagedata = {'mercurial': ['locale/*/LC_MESSAGES/hg.mo',
825 'help/*.txt',
825 'help/*.txt',
826 'help/internals/*.txt',
826 'help/internals/*.txt',
827 'default.d/*.rc',
827 'default.d/*.rc',
828 'dummycert.pem']}
828 'dummycert.pem']}
829
829
830 def ordinarypath(p):
830 def ordinarypath(p):
831 return p and p[0] != '.' and p[-1] != '~'
831 return p and p[0] != '.' and p[-1] != '~'
832
832
833 for root in ('templates',):
833 for root in ('templates',):
834 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
834 for curdir, dirs, files in os.walk(os.path.join('mercurial', root)):
835 curdir = curdir.split(os.sep, 1)[1]
835 curdir = curdir.split(os.sep, 1)[1]
836 dirs[:] = filter(ordinarypath, dirs)
836 dirs[:] = filter(ordinarypath, dirs)
837 for f in filter(ordinarypath, files):
837 for f in filter(ordinarypath, files):
838 f = os.path.join(curdir, f)
838 f = os.path.join(curdir, f)
839 packagedata['mercurial'].append(f)
839 packagedata['mercurial'].append(f)
840
840
841 datafiles = []
841 datafiles = []
842
842
843 # distutils expects version to be str/unicode. Converting it to
843 # distutils expects version to be str/unicode. Converting it to
844 # unicode on Python 2 still works because it won't contain any
844 # unicode on Python 2 still works because it won't contain any
845 # non-ascii bytes and will be implicitly converted back to bytes
845 # non-ascii bytes and will be implicitly converted back to bytes
846 # when operated on.
846 # when operated on.
847 assert isinstance(version, bytes)
847 assert isinstance(version, bytes)
848 setupversion = version.decode('ascii')
848 setupversion = version.decode('ascii')
849
849
850 extra = {}
850 extra = {}
851
851
852 if issetuptools:
852 if issetuptools:
853 extra['python_requires'] = supportedpy
853 extra['python_requires'] = supportedpy
854 if py2exeloaded:
854 if py2exeloaded:
855 extra['console'] = [
855 extra['console'] = [
856 {'script':'hg',
856 {'script':'hg',
857 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
857 'copyright':'Copyright (C) 2005-2017 Matt Mackall and others',
858 'product_version':version}]
858 'product_version':version}]
859 # sub command of 'build' because 'py2exe' does not handle sub_commands
859 # sub command of 'build' because 'py2exe' does not handle sub_commands
860 build.sub_commands.insert(0, ('build_hgextindex', None))
860 build.sub_commands.insert(0, ('build_hgextindex', None))
861 # put dlls in sub directory so that they won't pollute PATH
861 # put dlls in sub directory so that they won't pollute PATH
862 extra['zipfile'] = 'lib/library.zip'
862 extra['zipfile'] = 'lib/library.zip'
863
863
864 if os.name == 'nt':
864 if os.name == 'nt':
865 # Windows binary file versions for exe/dll files must have the
865 # Windows binary file versions for exe/dll files must have the
866 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
866 # form W.X.Y.Z, where W,X,Y,Z are numbers in the range 0..65535
867 setupversion = version.split('+', 1)[0]
867 setupversion = version.split('+', 1)[0]
868
868
869 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
869 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
870 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
870 version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
871 if version:
871 if version:
872 version = version[0]
872 version = version[0]
873 if sys.version_info[0] == 3:
873 if sys.version_info[0] == 3:
874 version = version.decode('utf-8')
874 version = version.decode('utf-8')
875 xcode4 = (version.startswith('Xcode') and
875 xcode4 = (version.startswith('Xcode') and
876 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
876 StrictVersion(version.split()[1]) >= StrictVersion('4.0'))
877 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
877 xcode51 = re.match(r'^Xcode\s+5\.1', version) is not None
878 else:
878 else:
879 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
879 # xcodebuild returns empty on OS X Lion with XCode 4.3 not
880 # installed, but instead with only command-line tools. Assume
880 # installed, but instead with only command-line tools. Assume
881 # that only happens on >= Lion, thus no PPC support.
881 # that only happens on >= Lion, thus no PPC support.
882 xcode4 = True
882 xcode4 = True
883 xcode51 = False
883 xcode51 = False
884
884
885 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
885 # XCode 4.0 dropped support for ppc architecture, which is hardcoded in
886 # distutils.sysconfig
886 # distutils.sysconfig
887 if xcode4:
887 if xcode4:
888 os.environ['ARCHFLAGS'] = ''
888 os.environ['ARCHFLAGS'] = ''
889
889
890 # XCode 5.1 changes clang such that it now fails to compile if the
890 # XCode 5.1 changes clang such that it now fails to compile if the
891 # -mno-fused-madd flag is passed, but the version of Python shipped with
891 # -mno-fused-madd flag is passed, but the version of Python shipped with
892 # OS X 10.9 Mavericks includes this flag. This causes problems in all
892 # OS X 10.9 Mavericks includes this flag. This causes problems in all
893 # C extension modules, and a bug has been filed upstream at
893 # C extension modules, and a bug has been filed upstream at
894 # http://bugs.python.org/issue21244. We also need to patch this here
894 # http://bugs.python.org/issue21244. We also need to patch this here
895 # so Mercurial can continue to compile in the meantime.
895 # so Mercurial can continue to compile in the meantime.
896 if xcode51:
896 if xcode51:
897 cflags = get_config_var('CFLAGS')
897 cflags = get_config_var('CFLAGS')
898 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
898 if cflags and re.search(r'-mno-fused-madd\b', cflags) is not None:
899 os.environ['CFLAGS'] = (
899 os.environ['CFLAGS'] = (
900 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
900 os.environ.get('CFLAGS', '') + ' -Qunused-arguments')
901
901
902 setup(name='mercurial',
902 setup(name='mercurial',
903 version=setupversion,
903 version=setupversion,
904 author='Matt Mackall and many others',
904 author='Matt Mackall and many others',
905 author_email='mercurial@mercurial-scm.org',
905 author_email='mercurial@mercurial-scm.org',
906 url='https://mercurial-scm.org/',
906 url='https://mercurial-scm.org/',
907 download_url='https://mercurial-scm.org/release/',
907 download_url='https://mercurial-scm.org/release/',
908 description=('Fast scalable distributed SCM (revision control, version '
908 description=('Fast scalable distributed SCM (revision control, version '
909 'control) system'),
909 'control) system'),
910 long_description=('Mercurial is a distributed SCM tool written in Python.'
910 long_description=('Mercurial is a distributed SCM tool written in Python.'
911 ' It is used by a number of large projects that require'
911 ' It is used by a number of large projects that require'
912 ' fast, reliable distributed revision control, such as '
912 ' fast, reliable distributed revision control, such as '
913 'Mozilla.'),
913 'Mozilla.'),
914 license='GNU GPLv2 or any later version',
914 license='GNU GPLv2 or any later version',
915 classifiers=[
915 classifiers=[
916 'Development Status :: 6 - Mature',
916 'Development Status :: 6 - Mature',
917 'Environment :: Console',
917 'Environment :: Console',
918 'Intended Audience :: Developers',
918 'Intended Audience :: Developers',
919 'Intended Audience :: System Administrators',
919 'Intended Audience :: System Administrators',
920 'License :: OSI Approved :: GNU General Public License (GPL)',
920 'License :: OSI Approved :: GNU General Public License (GPL)',
921 'Natural Language :: Danish',
921 'Natural Language :: Danish',
922 'Natural Language :: English',
922 'Natural Language :: English',
923 'Natural Language :: German',
923 'Natural Language :: German',
924 'Natural Language :: Italian',
924 'Natural Language :: Italian',
925 'Natural Language :: Japanese',
925 'Natural Language :: Japanese',
926 'Natural Language :: Portuguese (Brazilian)',
926 'Natural Language :: Portuguese (Brazilian)',
927 'Operating System :: Microsoft :: Windows',
927 'Operating System :: Microsoft :: Windows',
928 'Operating System :: OS Independent',
928 'Operating System :: OS Independent',
929 'Operating System :: POSIX',
929 'Operating System :: POSIX',
930 'Programming Language :: C',
930 'Programming Language :: C',
931 'Programming Language :: Python',
931 'Programming Language :: Python',
932 'Topic :: Software Development :: Version Control',
932 'Topic :: Software Development :: Version Control',
933 ],
933 ],
934 scripts=scripts,
934 scripts=scripts,
935 packages=packages,
935 packages=packages,
936 ext_modules=extmodules,
936 ext_modules=extmodules,
937 data_files=datafiles,
937 data_files=datafiles,
938 package_data=packagedata,
938 package_data=packagedata,
939 cmdclass=cmdclass,
939 cmdclass=cmdclass,
940 distclass=hgdist,
940 distclass=hgdist,
941 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
941 options={'py2exe': {'packages': ['hgdemandimport', 'hgext', 'email',
942 # implicitly imported per module policy
942 # implicitly imported per module policy
943 # (cffi wouldn't be used as a frozen exe)
943 # (cffi wouldn't be used as a frozen exe)
944 'mercurial.cext',
944 'mercurial.cext',
945 #'mercurial.cffi',
945 #'mercurial.cffi',
946 'mercurial.pure']},
946 'mercurial.pure']},
947 'bdist_mpkg': {'zipdist': False,
947 'bdist_mpkg': {'zipdist': False,
948 'license': 'COPYING',
948 'license': 'COPYING',
949 'readme': 'contrib/macosx/Readme.html',
949 'readme': 'contrib/macosx/Readme.html',
950 'welcome': 'contrib/macosx/Welcome.html',
950 'welcome': 'contrib/macosx/Welcome.html',
951 },
951 },
952 },
952 },
953 **extra)
953 **extra)
General Comments 0
You need to be logged in to leave comments. Login now