Mercurial > hg
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, ©map, &PyTuple_Type, &pl, | 884 if (!PyArg_ParseTuple(args, "O!O!O!(ii):pack_dirstate", &PyDict_Type, |
862 &now)) { | 885 &map, &PyDict_Type, ©map, &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); |