comparison mercurial/cext/parsers.c @ 48260:269ff8978086

dirstate: store mtimes with nanosecond precision in memory Keep integer seconds since the Unix epoch, together with integer nanoseconds in the `0 <= n < 1e9` range. For now, nanoseconds are still always zero. This commit is about data structure changes. Differential Revision: https://phab.mercurial-scm.org/D11684
author Simon Sapin <simon.sapin@octobus.net>
date Mon, 18 Oct 2021 11:23:07 +0200
parents 948570aa7630
children 9205d9be8b41
comparison
equal deleted inserted replaced
48259:84f6b0c41b90 48260:269ff8978086
55 int p2_info; 55 int p2_info;
56 int has_meaningful_data; 56 int has_meaningful_data;
57 int has_meaningful_mtime; 57 int has_meaningful_mtime;
58 int mode; 58 int mode;
59 int size; 59 int size;
60 int mtime; 60 int mtime_s;
61 int mtime_ns;
61 PyObject *parentfiledata; 62 PyObject *parentfiledata;
62 PyObject *fallback_exec; 63 PyObject *fallback_exec;
63 PyObject *fallback_symlink; 64 PyObject *fallback_symlink;
64 static char *keywords_name[] = { 65 static char *keywords_name[] = {
65 "wc_tracked", "p1_tracked", "p2_info", 66 "wc_tracked", "p1_tracked", "p2_info",
109 t->flags |= dirstate_flag_fallback_symlink; 110 t->flags |= dirstate_flag_fallback_symlink;
110 } 111 }
111 } 112 }
112 113
113 if (parentfiledata != Py_None) { 114 if (parentfiledata != Py_None) {
114 if (!PyTuple_CheckExact(parentfiledata)) { 115 if (!PyArg_ParseTuple(parentfiledata, "ii(ii)", &mode, &size,
115 PyErr_SetString( 116 &mtime_s, &mtime_ns)) {
116 PyExc_TypeError,
117 "parentfiledata should be a Tuple or None");
118 return NULL; 117 return NULL;
119 } 118 }
120 mode = (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
121 size = (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
122 mtime = (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
123 } else { 119 } else {
124 has_meaningful_data = 0; 120 has_meaningful_data = 0;
125 has_meaningful_mtime = 0; 121 has_meaningful_mtime = 0;
126 } 122 }
127 if (has_meaningful_data) { 123 if (has_meaningful_data) {
132 t->mode = 0; 128 t->mode = 0;
133 t->size = 0; 129 t->size = 0;
134 } 130 }
135 if (has_meaningful_mtime) { 131 if (has_meaningful_mtime) {
136 t->flags |= dirstate_flag_has_file_mtime; 132 t->flags |= dirstate_flag_has_file_mtime;
137 t->mtime = mtime; 133 t->mtime_s = mtime_s;
138 } else { 134 t->mtime_ns = mtime_ns;
139 t->mtime = 0; 135 } else {
136 t->mtime_s = 0;
137 t->mtime_ns = 0;
140 } 138 }
141 return (PyObject *)t; 139 return (PyObject *)t;
142 } 140 }
143 141
144 static void dirstate_item_dealloc(PyObject *o) 142 static void dirstate_item_dealloc(PyObject *o)
252 !(self->flags & dirstate_flag_p1_tracked) || 250 !(self->flags & dirstate_flag_p1_tracked) ||
253 !(self->flags & dirstate_flag_wc_tracked) || 251 !(self->flags & dirstate_flag_wc_tracked) ||
254 (self->flags & dirstate_flag_p2_info)) { 252 (self->flags & dirstate_flag_p2_info)) {
255 return ambiguous_time; 253 return ambiguous_time;
256 } else { 254 } else {
257 return self->mtime; 255 return self->mtime_s;
258 } 256 }
259 } 257 }
260 258
261 static PyObject *dirstate_item_v2_data(dirstateItemObject *self) 259 static PyObject *dirstate_item_v2_data(dirstateItemObject *self)
262 { 260 {
270 if (S_ISLNK(mode)) { 268 if (S_ISLNK(mode)) {
271 flags |= dirstate_flag_mode_is_symlink; 269 flags |= dirstate_flag_mode_is_symlink;
272 } else { 270 } else {
273 flags &= ~dirstate_flag_mode_is_symlink; 271 flags &= ~dirstate_flag_mode_is_symlink;
274 } 272 }
275 return Py_BuildValue("iii", flags, self->size, self->mtime); 273 return Py_BuildValue("iiii", flags, self->size, self->mtime_s,
274 self->mtime_ns);
276 }; 275 };
277 276
278 static PyObject *dirstate_item_v1_state(dirstateItemObject *self) 277 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
279 { 278 {
280 char state = dirstate_item_c_v1_state(self); 279 char state = dirstate_item_c_v1_state(self);
295 { 294 {
296 return PyInt_FromLong(dirstate_item_c_v1_mtime(self)); 295 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
297 }; 296 };
298 297
299 static PyObject *dirstate_item_need_delay(dirstateItemObject *self, 298 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
300 PyObject *value) 299 PyObject *now)
301 { 300 {
302 long now; 301 int now_s;
303 if (!pylong_to_long(value, &now)) { 302 int now_ns;
304 return NULL; 303 if (!PyArg_ParseTuple(now, "ii", &now_s, &now_ns)) {
305 } 304 return NULL;
306 if (dirstate_item_c_v1_state(self) == 'n' && 305 }
307 dirstate_item_c_v1_mtime(self) == now) { 306 if (dirstate_item_c_v1_state(self) == 'n' && self->mtime_s == now_s) {
307 Py_RETURN_TRUE;
308 } else {
309 Py_RETURN_FALSE;
310 }
311 };
312
313 static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self,
314 PyObject *other)
315 {
316 int other_s;
317 int other_ns;
318 if (!PyArg_ParseTuple(other, "ii", &other_s, &other_ns)) {
319 return NULL;
320 }
321 if ((self->flags & dirstate_flag_has_file_mtime) &&
322 self->mtime_s == other_s && self->mtime_ns == other_ns) {
308 Py_RETURN_TRUE; 323 Py_RETURN_TRUE;
309 } else { 324 } else {
310 Py_RETURN_FALSE; 325 Py_RETURN_FALSE;
311 } 326 }
312 }; 327 };
322 return NULL; 337 return NULL;
323 } 338 }
324 t->flags = 0; 339 t->flags = 0;
325 t->mode = 0; 340 t->mode = 0;
326 t->size = 0; 341 t->size = 0;
327 t->mtime = 0; 342 t->mtime_s = 0;
343 t->mtime_ns = 0;
328 344
329 if (state == 'm') { 345 if (state == 'm') {
330 t->flags = (dirstate_flag_wc_tracked | 346 t->flags = (dirstate_flag_wc_tracked |
331 dirstate_flag_p1_tracked | dirstate_flag_p2_info); 347 dirstate_flag_p1_tracked | dirstate_flag_p2_info);
332 } else if (state == 'a') { 348 } else if (state == 'a') {
358 dirstate_flag_p1_tracked | 374 dirstate_flag_p1_tracked |
359 dirstate_flag_has_meaningful_data | 375 dirstate_flag_has_meaningful_data |
360 dirstate_flag_has_file_mtime); 376 dirstate_flag_has_file_mtime);
361 t->mode = mode; 377 t->mode = mode;
362 t->size = size; 378 t->size = size;
363 t->mtime = mtime; 379 t->mtime_s = mtime;
364 } 380 }
365 } else { 381 } else {
366 PyErr_Format(PyExc_RuntimeError, 382 PyErr_Format(PyExc_RuntimeError,
367 "unknown state: `%c` (%d, %d, %d)", state, mode, 383 "unknown state: `%c` (%d, %d, %d)", state, mode,
368 size, mtime, NULL); 384 size, mtime, NULL);
393 dirstateItemObject *t = 409 dirstateItemObject *t =
394 PyObject_New(dirstateItemObject, &dirstateItemType); 410 PyObject_New(dirstateItemObject, &dirstateItemType);
395 if (!t) { 411 if (!t) {
396 return NULL; 412 return NULL;
397 } 413 }
398 if (!PyArg_ParseTuple(args, "iii", &t->flags, &t->size, &t->mtime)) { 414 if (!PyArg_ParseTuple(args, "iiii", &t->flags, &t->size, &t->mtime_s,
415 &t->mtime_ns)) {
399 return NULL; 416 return NULL;
400 } 417 }
401 if (t->flags & dirstate_flag_expected_state_is_modified) { 418 if (t->flags & dirstate_flag_expected_state_is_modified) {
402 t->flags &= ~(dirstate_flag_expected_state_is_modified | 419 t->flags &= ~(dirstate_flag_expected_state_is_modified |
403 dirstate_flag_has_meaningful_data | 420 dirstate_flag_has_meaningful_data |
429 446
430 /* See docstring of the python implementation for details */ 447 /* See docstring of the python implementation for details */
431 static PyObject *dirstate_item_set_clean(dirstateItemObject *self, 448 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
432 PyObject *args) 449 PyObject *args)
433 { 450 {
434 int size, mode, mtime; 451 int size, mode, mtime_s, mtime_ns;
435 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) { 452 if (!PyArg_ParseTuple(args, "ii(ii)", &mode, &size, &mtime_s,
453 &mtime_ns)) {
436 return NULL; 454 return NULL;
437 } 455 }
438 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | 456 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
439 dirstate_flag_has_meaningful_data | 457 dirstate_flag_has_meaningful_data |
440 dirstate_flag_has_file_mtime; 458 dirstate_flag_has_file_mtime;
441 self->mode = mode; 459 self->mode = mode;
442 self->size = size; 460 self->size = size;
443 self->mtime = mtime; 461 self->mtime_s = mtime_s;
462 self->mtime_ns = mtime_ns;
444 Py_RETURN_NONE; 463 Py_RETURN_NONE;
445 } 464 }
446 465
447 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self) 466 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
448 { 467 {
453 472
454 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self) 473 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
455 { 474 {
456 self->flags &= ~dirstate_flag_wc_tracked; 475 self->flags &= ~dirstate_flag_wc_tracked;
457 self->mode = 0; 476 self->mode = 0;
458 self->mtime = 0;
459 self->size = 0; 477 self->size = 0;
478 self->mtime_s = 0;
479 self->mtime_ns = 0;
460 Py_RETURN_NONE; 480 Py_RETURN_NONE;
461 } 481 }
462 482
463 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self) 483 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self)
464 { 484 {
465 if (self->flags & dirstate_flag_p2_info) { 485 if (self->flags & dirstate_flag_p2_info) {
466 self->flags &= ~(dirstate_flag_p2_info | 486 self->flags &= ~(dirstate_flag_p2_info |
467 dirstate_flag_has_meaningful_data | 487 dirstate_flag_has_meaningful_data |
468 dirstate_flag_has_file_mtime); 488 dirstate_flag_has_file_mtime);
469 self->mode = 0; 489 self->mode = 0;
470 self->mtime = 0;
471 self->size = 0; 490 self->size = 0;
491 self->mtime_s = 0;
492 self->mtime_ns = 0;
472 } 493 }
473 Py_RETURN_NONE; 494 Py_RETURN_NONE;
474 } 495 }
475 static PyMethodDef dirstate_item_methods[] = { 496 static PyMethodDef dirstate_item_methods[] = {
476 {"v2_data", (PyCFunction)dirstate_item_v2_data, METH_NOARGS, 497 {"v2_data", (PyCFunction)dirstate_item_v2_data, METH_NOARGS,
483 "return a \"size\" suitable for v1 serialization"}, 504 "return a \"size\" suitable for v1 serialization"},
484 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS, 505 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
485 "return a \"mtime\" suitable for v1 serialization"}, 506 "return a \"mtime\" suitable for v1 serialization"},
486 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O, 507 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
487 "True if the stored mtime would be ambiguous with the current time"}, 508 "True if the stored mtime would be ambiguous with the current time"},
509 {"mtime_likely_equal_to", (PyCFunction)dirstate_item_mtime_likely_equal_to,
510 METH_O, "True if the stored mtime is likely equal to the given mtime"},
488 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth, 511 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
489 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"}, 512 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
490 {"from_v2_data", (PyCFunction)dirstate_item_from_v2_meth, 513 {"from_v2_data", (PyCFunction)dirstate_item_from_v2_meth,
491 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V2 data"}, 514 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V2 data"},
492 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty, 515 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
853 PyObject *packobj = NULL; 876 PyObject *packobj = NULL;
854 PyObject *map, *copymap, *pl, *mtime_unset = NULL; 877 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
855 Py_ssize_t nbytes, pos, l; 878 Py_ssize_t nbytes, pos, l;
856 PyObject *k, *v = NULL, *pn; 879 PyObject *k, *v = NULL, *pn;
857 char *p, *s; 880 char *p, *s;
858 int now; 881 int now_s;
859 882 int now_ns;
860 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map, 883
861 &PyDict_Type, &copymap, &PyTuple_Type, &pl, 884 if (!PyArg_ParseTuple(args, "O!O!O!(ii):pack_dirstate", &PyDict_Type,
862 &now)) { 885 &map, &PyDict_Type, &copymap, &PyTuple_Type, &pl,
886 &now_s, &now_ns)) {
863 return NULL; 887 return NULL;
864 } 888 }
865 889
866 if (PyTuple_Size(pl) != 2) { 890 if (PyTuple_Size(pl) != 2) {
867 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple"); 891 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
926 950
927 state = dirstate_item_c_v1_state(tuple); 951 state = dirstate_item_c_v1_state(tuple);
928 mode = dirstate_item_c_v1_mode(tuple); 952 mode = dirstate_item_c_v1_mode(tuple);
929 size = dirstate_item_c_v1_size(tuple); 953 size = dirstate_item_c_v1_size(tuple);
930 mtime = dirstate_item_c_v1_mtime(tuple); 954 mtime = dirstate_item_c_v1_mtime(tuple);
931 if (state == 'n' && mtime == now) { 955 if (state == 'n' && tuple->mtime_s == now_s) {
932 /* See pure/parsers.py:pack_dirstate for why we do 956 /* See pure/parsers.py:pack_dirstate for why we do
933 * this. */ 957 * this. */
934 mtime = -1; 958 mtime = -1;
935 mtime_unset = (PyObject *)dirstate_item_from_v1_data( 959 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
936 state, mode, size, mtime); 960 state, mode, size, mtime);