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