##// END OF EJS Templates
dirstate: add a comment about a racy piece of code during updates...
dirstate: add a comment about a racy piece of code during updates This is a bit that is not really correct but works "fine" in practice. Let us write the details down so that people stop wondering how that logic might be correct… It is not. Differential Revision: https://phab.mercurial-scm.org/D11781

File last commit:

r49149:3620ab28 stable
r49200:c655483e default
Show More
parsers.c
1337 lines | 36.7 KiB | text/x-c | CLexer
Yuya Nishihara
parsers: switch to policy importer...
r32372 /*
parsers.c - efficient content parsing
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
Yuya Nishihara
parsers: switch to policy importer...
r32372
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 #define PY_SSIZE_T_CLEAN
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include <Python.h>
#include <ctype.h>
#include <stddef.h>
#include <string.h>
Gregory Szorc
cext: reorder #include...
r34439 #include "bitmanipulation.h"
Yuya Nishihara
cext: factor out header for charencode.c...
r33753 #include "charencode.h"
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include "util.h"
#ifdef IS_PY3K
/* The mapping of Python types is meant to be temporary to get Python
* 3 to compile. We should remove this once Python 3 support is fully
* supported and proper types are used in the extensions themselves. */
#define PyInt_Check PyLong_Check
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyInt_AsLong PyLong_AsLong
Gregory Szorc
cext: define S_IFLNK on Python 2.7 and Windows...
r49149 #else
/* Windows on Python 2.7 doesn't define S_IFLNK. Python 3+ defines via
* pyport.h. */
#ifndef S_IFLNK
#define S_IFLNK 0120000
#endif
Yuya Nishihara
parsers: switch to policy importer...
r32372 #endif
Yuya Nishihara
cext: mark constant variables
r32385 static const char *const versionerrortext = "Python minor version mismatch";
Yuya Nishihara
parsers: switch to policy importer...
r32372
dirstate-entry: add a `from_p2` property...
r48303 static const int dirstate_v1_from_p2 = -2;
dirstate-entry: `merged_removed` and `from_p2_removed` properties...
r48305 static const int dirstate_v1_nonnormal = -1;
dirstate-item: add a `set_possibly_dirty` method...
r48466 static const int ambiguous_time = -1;
dirstate-entry: add a `from_p2` property...
r48303
Yuya Nishihara
parsers: switch to policy importer...
r32372 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
{
Py_ssize_t expected_size;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
return _dict_new_presized(expected_size);
}
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
PyObject *kwds)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
/* We do all the initialization here and not a tp_init function because
dirstate-item: rename the class to DirstateItem...
r48328 * dirstate_item is immutable. */
dirstateItemObject *t;
dirstate-item: feed more information to `__init__`...
r48706 int wc_tracked;
int p1_tracked;
dirstate-item: change the internal storage and constructor value...
r48950 int p2_info;
int has_meaningful_data;
int has_meaningful_mtime;
int mode;
int size;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 int mtime_s;
int mtime_ns;
dirstate-item: feed more information to `__init__`...
r48706 PyObject *parentfiledata;
dirstate: make DirstateItem constructor accept fallback value...
r49069 PyObject *fallback_exec;
PyObject *fallback_symlink;
dirstate-item: feed more information to `__init__`...
r48706 static char *keywords_name[] = {
dirstate: make DirstateItem constructor accept fallback value...
r49069 "wc_tracked", "p1_tracked", "p2_info",
"has_meaningful_data", "has_meaningful_mtime", "parentfiledata",
"fallback_exec", "fallback_symlink", NULL,
dirstate-item: feed more information to `__init__`...
r48706 };
wc_tracked = 0;
p1_tracked = 0;
dirstate-item: change the internal storage and constructor value...
r48950 p2_info = 0;
has_meaningful_mtime = 1;
has_meaningful_data = 1;
dirstate-item: feed more information to `__init__`...
r48706 parentfiledata = Py_None;
dirstate: make DirstateItem constructor accept fallback value...
r49069 fallback_exec = Py_None;
fallback_symlink = Py_None;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiiiOOO", keywords_name,
&wc_tracked, &p1_tracked, &p2_info,
&has_meaningful_data,
&has_meaningful_mtime, &parentfiledata,
&fallback_exec, &fallback_symlink)) {
dirstate-item: feed more information to `__init__`...
r48706 return NULL;
}
dirstate-item: rename the class to DirstateItem...
r48328 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!t) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
dirstate-item: move the C implementation to the same logic...
r48760
t->flags = 0;
if (wc_tracked) {
t->flags |= dirstate_flag_wc_tracked;
}
if (p1_tracked) {
t->flags |= dirstate_flag_p1_tracked;
}
dirstate-item: change the internal storage and constructor value...
r48950 if (p2_info) {
t->flags |= dirstate_flag_p2_info;
dirstate-item: move the C implementation to the same logic...
r48760 }
dirstate-item: change the internal storage and constructor value...
r48950
dirstate: make DirstateItem constructor accept fallback value...
r49069 if (fallback_exec != Py_None) {
t->flags |= dirstate_flag_has_fallback_exec;
if (PyObject_IsTrue(fallback_exec)) {
t->flags |= dirstate_flag_fallback_exec;
}
}
if (fallback_symlink != Py_None) {
t->flags |= dirstate_flag_has_fallback_symlink;
if (PyObject_IsTrue(fallback_symlink)) {
t->flags |= dirstate_flag_fallback_symlink;
}
}
dirstate-item: move the C implementation to the same logic...
r48760 if (parentfiledata != Py_None) {
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 if (!PyArg_ParseTuple(parentfiledata, "ii(ii)", &mode, &size,
&mtime_s, &mtime_ns)) {
dirstate-item: feed more information to `__init__`...
r48706 return NULL;
}
dirstate-item: change the internal storage and constructor value...
r48950 } else {
has_meaningful_data = 0;
has_meaningful_mtime = 0;
}
if (has_meaningful_data) {
t->flags |= dirstate_flag_has_meaningful_data;
t->mode = mode;
t->size = size;
} else {
t->mode = 0;
t->size = 0;
}
if (has_meaningful_mtime) {
dirstate-v2: adjust the meaning of directory flags...
r49083 t->flags |= dirstate_flag_has_mtime;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 t->mtime_s = mtime_s;
t->mtime_ns = mtime_ns;
dirstate-item: change the internal storage and constructor value...
r48950 } else {
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 t->mtime_s = 0;
t->mtime_ns = 0;
dirstate-item: feed more information to `__init__`...
r48706 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 return (PyObject *)t;
}
dirstate-item: rename the class to DirstateItem...
r48328 static void dirstate_item_dealloc(PyObject *o)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
PyObject_Del(o);
}
dirstate-item: introduce low level C function...
r48759 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
{
dirstate-item: move the C implementation to the same logic...
r48760 return (self->flags & dirstate_flag_wc_tracked);
dirstate-item: introduce low level C function...
r48759 }
dirstate-item: change the internal storage and constructor value...
r48950 static inline bool dirstate_item_c_any_tracked(dirstateItemObject *self)
{
Simon Sapin
dirstate-v2: adds a flag to mark a file as modified...
r49066 const int mask = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
dirstate_flag_p2_info;
dirstate-item: change the internal storage and constructor value...
r48950 return (self->flags & mask);
}
dirstate-item: introduce low level C function...
r48759 static inline bool dirstate_item_c_added(dirstateItemObject *self)
{
Simon Sapin
dirstate-v2: adds a flag to mark a file as modified...
r49066 const int mask = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
dirstate_flag_p2_info);
const int target = dirstate_flag_wc_tracked;
dirstate-item: move the C implementation to the same logic...
r48760 return (self->flags & mask) == target;
dirstate-item: introduce low level C function...
r48759 }
static inline bool dirstate_item_c_removed(dirstateItemObject *self)
{
dirstate-item: move the C implementation to the same logic...
r48760 if (self->flags & dirstate_flag_wc_tracked) {
return false;
}
return (self->flags &
dirstate-item: change the internal storage and constructor value...
r48950 (dirstate_flag_p1_tracked | dirstate_flag_p2_info));
dirstate-item: introduce low level C function...
r48759 }
static inline bool dirstate_item_c_merged(dirstateItemObject *self)
{
dirstate-item: move the C implementation to the same logic...
r48760 return ((self->flags & dirstate_flag_wc_tracked) &&
dirstate-item: change the internal storage and constructor value...
r48950 (self->flags & dirstate_flag_p1_tracked) &&
(self->flags & dirstate_flag_p2_info));
dirstate-item: introduce low level C function...
r48759 }
static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
{
dirstate-item: change the internal storage and constructor value...
r48950 return ((self->flags & dirstate_flag_wc_tracked) &&
!(self->flags & dirstate_flag_p1_tracked) &&
(self->flags & dirstate_flag_p2_info));
dirstate-item: introduce low level C function...
r48759 }
static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
{
Simon Sapin
dirstate: Remove the `state == ' '` special case...
r48837 if (dirstate_item_c_removed(self)) {
dirstate-item: move the C implementation to the same logic...
r48760 return 'r';
} else if (dirstate_item_c_merged(self)) {
return 'm';
} else if (dirstate_item_c_added(self)) {
return 'a';
} else {
return 'n';
}
dirstate-item: introduce low level C function...
r48759 }
dirstate: add a concept of "fallback" flags to dirstate item...
r49068 static inline bool dirstate_item_c_has_fallback_exec(dirstateItemObject *self)
{
return (bool)self->flags & dirstate_flag_has_fallback_exec;
}
static inline bool
dirstate_item_c_has_fallback_symlink(dirstateItemObject *self)
{
return (bool)self->flags & dirstate_flag_has_fallback_symlink;
}
dirstate-item: introduce low level C function...
r48759 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
{
dirstate-item: change the internal storage and constructor value...
r48950 if (self->flags & dirstate_flag_has_meaningful_data) {
return self->mode;
} else {
return 0;
}
dirstate-item: introduce low level C function...
r48759 }
static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
{
dirstate-item: change the internal storage and constructor value...
r48950 if (!(self->flags & dirstate_flag_wc_tracked) &&
(self->flags & dirstate_flag_p2_info)) {
if (self->flags & dirstate_flag_p1_tracked) {
return dirstate_v1_nonnormal;
} else {
return dirstate_v1_from_p2;
}
dirstate-item: move the C implementation to the same logic...
r48760 } else if (dirstate_item_c_removed(self)) {
return 0;
dirstate-item: change the internal storage and constructor value...
r48950 } else if (self->flags & dirstate_flag_p2_info) {
dirstate-item: move the C implementation to the same logic...
r48760 return dirstate_v1_from_p2;
} else if (dirstate_item_c_added(self)) {
return dirstate_v1_nonnormal;
dirstate-item: change the internal storage and constructor value...
r48950 } else if (self->flags & dirstate_flag_has_meaningful_data) {
return self->size;
dirstate-item: move the C implementation to the same logic...
r48760 } else {
dirstate-item: change the internal storage and constructor value...
r48950 return dirstate_v1_nonnormal;
dirstate-item: move the C implementation to the same logic...
r48760 }
dirstate-item: introduce low level C function...
r48759 }
static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
{
Simon Sapin
dirstate: Remove the `state == ' '` special case...
r48837 if (dirstate_item_c_removed(self)) {
dirstate-item: move the C implementation to the same logic...
r48760 return 0;
dirstate-v2: adjust the meaning of directory flags...
r49083 } else if (!(self->flags & dirstate_flag_has_mtime) ||
dirstate-item: change the internal storage and constructor value...
r48950 !(self->flags & dirstate_flag_p1_tracked) ||
!(self->flags & dirstate_flag_wc_tracked) ||
(self->flags & dirstate_flag_p2_info)) {
dirstate-item: move the C implementation to the same logic...
r48760 return ambiguous_time;
} else {
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 return self->mtime_s;
dirstate-item: move the C implementation to the same logic...
r48760 }
dirstate-item: introduce low level C function...
r48759 }
Raphaël Gomès
dirstate-v2: Initial Python serializer...
r49036 static PyObject *dirstate_item_v2_data(dirstateItemObject *self)
{
Simon Sapin
dirstate-v2: adds a flag to mark a file as modified...
r49066 int flags = self->flags;
Raphaël Gomès
dirstate-v2: Initial Python serializer...
r49036 int mode = dirstate_item_c_v1_mode(self);
Raphaël Gomès
parsers: don't ask about the exec bit on platforms that don't have it...
r49100 #ifdef S_IXUSR
/* This is for platforms with an exec bit */
Raphaël Gomès
dirstate-v2: Initial Python serializer...
r49036 if ((mode & S_IXUSR) != 0) {
flags |= dirstate_flag_mode_exec_perm;
} else {
flags &= ~dirstate_flag_mode_exec_perm;
}
Raphaël Gomès
parsers: don't ask about the exec bit on platforms that don't have it...
r49100 #else
flags &= ~dirstate_flag_mode_exec_perm;
#endif
Raphaël Gomès
parsers: don't ask about symlinks on platforms that don't support them...
r49101 #ifdef S_ISLNK
/* This is for platforms with support for symlinks */
Raphaël Gomès
dirstate-v2: Initial Python serializer...
r49036 if (S_ISLNK(mode)) {
flags |= dirstate_flag_mode_is_symlink;
} else {
flags &= ~dirstate_flag_mode_is_symlink;
}
Raphaël Gomès
parsers: don't ask about symlinks on platforms that don't support them...
r49101 #else
flags &= ~dirstate_flag_mode_is_symlink;
#endif
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 return Py_BuildValue("iiii", flags, self->size, self->mtime_s,
self->mtime_ns);
Raphaël Gomès
dirstate-v2: Initial Python serializer...
r49036 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 {
dirstate-item: introduce low level C function...
r48759 char state = dirstate_item_c_v1_state(self);
return PyBytes_FromStringAndSize(&state, 1);
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 {
dirstate-item: introduce low level C function...
r48759 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 {
dirstate-item: introduce low level C function...
r48759 return PyInt_FromLong(dirstate_item_c_v1_size(self));
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 {
dirstate-item: introduce low level C function...
r48759 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 PyObject *now)
dirstate-entry: add a `need_delay` method...
r48321 {
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 int now_s;
int now_ns;
if (!PyArg_ParseTuple(now, "ii", &now_s, &now_ns)) {
dirstate-entry: add a `need_delay` method...
r48321 return NULL;
}
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 if (dirstate_item_c_v1_state(self) == 'n' && self->mtime_s == now_s) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self,
PyObject *other)
{
int other_s;
int other_ns;
if (!PyArg_ParseTuple(other, "ii", &other_s, &other_ns)) {
return NULL;
}
dirstate-v2: adjust the meaning of directory flags...
r49083 if ((self->flags & dirstate_flag_has_mtime) &&
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 self->mtime_s == other_s &&
(self->mtime_ns == other_ns || self->mtime_ns == 0 ||
other_ns == 0)) {
dirstate-entry: add a `need_delay` method...
r48321 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: `dirstate_item_from_v1_data` replaces make_dirstate_item...
r48757 /* This will never change since it's bound to V1
dirstate-item: add a `from_v1_data` constructor...
r48465 */
static inline dirstateItemObject *
dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
{
dirstateItemObject *t =
PyObject_New(dirstateItemObject, &dirstateItemType);
if (!t) {
return NULL;
}
dirstate-item: change the internal storage and constructor value...
r48950 t->flags = 0;
t->mode = 0;
t->size = 0;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 t->mtime_s = 0;
t->mtime_ns = 0;
dirstate-item: move the C implementation to the same logic...
r48760
if (state == 'm') {
dirstate-item: change the internal storage and constructor value...
r48950 t->flags = (dirstate_flag_wc_tracked |
dirstate_flag_p1_tracked | dirstate_flag_p2_info);
dirstate-item: move the C implementation to the same logic...
r48760 } else if (state == 'a') {
t->flags = dirstate_flag_wc_tracked;
} else if (state == 'r') {
if (size == dirstate_v1_nonnormal) {
t->flags =
dirstate-item: change the internal storage and constructor value...
r48950 dirstate_flag_p1_tracked | dirstate_flag_p2_info;
dirstate-item: move the C implementation to the same logic...
r48760 } else if (size == dirstate_v1_from_p2) {
dirstate-item: change the internal storage and constructor value...
r48950 t->flags = dirstate_flag_p2_info;
dirstate-item: move the C implementation to the same logic...
r48760 } else {
t->flags = dirstate_flag_p1_tracked;
}
} else if (state == 'n') {
if (size == dirstate_v1_from_p2) {
t->flags =
dirstate-item: change the internal storage and constructor value...
r48950 dirstate_flag_wc_tracked | dirstate_flag_p2_info;
dirstate-item: move the C implementation to the same logic...
r48760 } else if (size == dirstate_v1_nonnormal) {
dirstate-item: change the internal storage and constructor value...
r48950 t->flags =
dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
dirstate-item: move the C implementation to the same logic...
r48760 } else if (mtime == ambiguous_time) {
t->flags = (dirstate_flag_wc_tracked |
dirstate_flag_p1_tracked |
dirstate-item: change the internal storage and constructor value...
r48950 dirstate_flag_has_meaningful_data);
dirstate-item: move the C implementation to the same logic...
r48760 t->mode = mode;
t->size = size;
} else {
t->flags = (dirstate_flag_wc_tracked |
dirstate-item: change the internal storage and constructor value...
r48950 dirstate_flag_p1_tracked |
dirstate_flag_has_meaningful_data |
dirstate-v2: adjust the meaning of directory flags...
r49083 dirstate_flag_has_mtime);
dirstate-item: move the C implementation to the same logic...
r48760 t->mode = mode;
t->size = size;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 t->mtime_s = mtime;
dirstate-item: move the C implementation to the same logic...
r48760 }
} else {
PyErr_Format(PyExc_RuntimeError,
"unknown state: `%c` (%d, %d, %d)", state, mode,
size, mtime, NULL);
Yuya Nishihara
dirstate: fix leak of entry object in dirstate_item_from_v1_data()
r48840 Py_DECREF(t);
dirstate-item: move the C implementation to the same logic...
r48760 return NULL;
}
dirstate-item: add a `from_v1_data` constructor...
r48465 return t;
}
/* This will never change since it's bound to V1, unlike `dirstate_item_new` */
static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
PyObject *args)
{
/* We do all the initialization here and not a tp_init function because
* dirstate_item is immutable. */
char state;
int size, mode, mtime;
if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
return NULL;
}
dirstate-item: factor some code in the C implementation...
r48758 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
dirstate-item: add a `from_v1_data` constructor...
r48465 };
Simon Sapin
dirstate-v2: initial Python parser...
r49035 static PyObject *dirstate_item_from_v2_meth(PyTypeObject *subtype,
PyObject *args)
{
dirstateItemObject *t =
PyObject_New(dirstateItemObject, &dirstateItemType);
if (!t) {
return NULL;
}
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 if (!PyArg_ParseTuple(args, "iiii", &t->flags, &t->size, &t->mtime_s,
&t->mtime_ns)) {
Simon Sapin
dirstate-v2: initial Python parser...
r49035 return NULL;
}
Simon Sapin
dirstate-v2: adds a flag to mark a file as modified...
r49066 if (t->flags & dirstate_flag_expected_state_is_modified) {
t->flags &= ~(dirstate_flag_expected_state_is_modified |
dirstate_flag_has_meaningful_data |
dirstate-v2: adjust the meaning of directory flags...
r49083 dirstate_flag_has_mtime);
Simon Sapin
dirstate-v2: adds a flag to mark a file as modified...
r49066 }
dirstate-v2: add a new MTIME_SECOND_AMBIGUOUS flags...
r49080 if (t->flags & dirstate_flag_mtime_second_ambiguous) {
/* The current code is not able to do the more subtle comparison
* that the MTIME_SECOND_AMBIGUOUS requires. So we ignore the
* mtime */
t->flags &= ~(dirstate_flag_mtime_second_ambiguous |
dirstate_flag_has_meaningful_data |
dirstate-v2: adjust the meaning of directory flags...
r49083 dirstate_flag_has_mtime);
dirstate-v2: add a new MTIME_SECOND_AMBIGUOUS flags...
r49080 }
Simon Sapin
dirstate-v2: initial Python parser...
r49035 t->mode = 0;
if (t->flags & dirstate_flag_has_meaningful_data) {
if (t->flags & dirstate_flag_mode_exec_perm) {
t->mode = 0755;
} else {
t->mode = 0644;
}
if (t->flags & dirstate_flag_mode_is_symlink) {
t->mode |= S_IFLNK;
} else {
t->mode |= S_IFREG;
}
}
return (PyObject *)t;
};
dirstate-item: add a `set_possibly_dirty` method...
r48466 /* This means the next status call will have to actually check its content
to make sure it is correct. */
static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
{
dirstate-v2: adjust the meaning of directory flags...
r49083 self->flags &= ~dirstate_flag_has_mtime;
Martin von Zweigbergk
dirstate: fix compilation warnings in `dirstate_item_set_possibly_dirty()`...
r48790 Py_RETURN_NONE;
dirstate-item: add a `set_possibly_dirty` method...
r48466 }
dirstate: introduce a `set_clean` method on dirstate's map and items...
r48788 /* See docstring of the python implementation for details */
static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
PyObject *args)
{
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 int size, mode, mtime_s, mtime_ns;
if (!PyArg_ParseTuple(args, "ii(ii)", &mode, &size, &mtime_s,
&mtime_ns)) {
dirstate: introduce a `set_clean` method on dirstate's map and items...
r48788 return NULL;
}
dirstate-item: change the internal storage and constructor value...
r48950 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
dirstate_flag_has_meaningful_data |
dirstate-v2: adjust the meaning of directory flags...
r49083 dirstate_flag_has_mtime;
dirstate: introduce a `set_clean` method on dirstate's map and items...
r48788 self->mode = mode;
self->size = size;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 self->mtime_s = mtime_s;
self->mtime_ns = mtime_ns;
dirstate: introduce a `set_clean` method on dirstate's map and items...
r48788 Py_RETURN_NONE;
}
dirstate: introduce a set_tracked method on "map" and "item"...
r48804 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
{
self->flags |= dirstate_flag_wc_tracked;
dirstate-v2: adjust the meaning of directory flags...
r49083 self->flags &= ~dirstate_flag_has_mtime;
dirstate: introduce a set_tracked method on "map" and "item"...
r48804 Py_RETURN_NONE;
}
dirstatemap: replace `removefile` by an explicit `entry.set_untracked()`...
r48701 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
{
dirstate-item: move the C implementation to the same logic...
r48760 self->flags &= ~dirstate_flag_wc_tracked;
dirstatemap: replace `removefile` by an explicit `entry.set_untracked()`...
r48701 self->mode = 0;
dirstate-item: move the C implementation to the same logic...
r48760 self->size = 0;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 self->mtime_s = 0;
self->mtime_ns = 0;
dirstatemap: replace `removefile` by an explicit `entry.set_untracked()`...
r48701 Py_RETURN_NONE;
}
dirstate: use a new `drop_merge_data` in `setparent`...
r48874 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self)
{
dirstate-item: change the internal storage and constructor value...
r48950 if (self->flags & dirstate_flag_p2_info) {
self->flags &= ~(dirstate_flag_p2_info |
dirstate_flag_has_meaningful_data |
dirstate-v2: adjust the meaning of directory flags...
r49083 dirstate_flag_has_mtime);
dirstate: use a new `drop_merge_data` in `setparent`...
r48874 self->mode = 0;
dirstate-item: change the internal storage and constructor value...
r48950 self->size = 0;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 self->mtime_s = 0;
self->mtime_ns = 0;
dirstate: use a new `drop_merge_data` in `setparent`...
r48874 }
Py_RETURN_NONE;
}
dirstate-item: rename the class to DirstateItem...
r48328 static PyMethodDef dirstate_item_methods[] = {
Raphaël Gomès
dirstate-v2: Initial Python serializer...
r49036 {"v2_data", (PyCFunction)dirstate_item_v2_data, METH_NOARGS,
"return data suitable for v2 serialization"},
dirstate-item: rename the class to DirstateItem...
r48328 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 "return a \"state\" suitable for v1 serialization"},
dirstate-item: rename the class to DirstateItem...
r48328 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 "return a \"mode\" suitable for v1 serialization"},
dirstate-item: rename the class to DirstateItem...
r48328 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 "return a \"size\" suitable for v1 serialization"},
dirstate-item: rename the class to DirstateItem...
r48328 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 "return a \"mtime\" suitable for v1 serialization"},
dirstate-item: rename the class to DirstateItem...
r48328 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
dirstate-entry: add a `need_delay` method...
r48321 "True if the stored mtime would be ambiguous with the current time"},
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 {"mtime_likely_equal_to", (PyCFunction)dirstate_item_mtime_likely_equal_to,
METH_O, "True if the stored mtime is likely equal to the given mtime"},
dirstate-item: fix the declaration of the Cext `from_v1_meth`...
r48703 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
Simon Sapin
dirstate-v2: initial Python parser...
r49035 {"from_v2_data", (PyCFunction)dirstate_item_from_v2_meth,
METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V2 data"},
dirstate-item: add a `set_possibly_dirty` method...
r48466 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
METH_NOARGS, "mark a file as \"possibly dirty\""},
dirstate: introduce a `set_clean` method on dirstate's map and items...
r48788 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
"mark a file as \"clean\""},
dirstate: introduce a set_tracked method on "map" and "item"...
r48804 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
"mark a file as \"tracked\""},
dirstatemap: replace `removefile` by an explicit `entry.set_untracked()`...
r48701 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
"mark a file as \"untracked\""},
dirstate: use a new `drop_merge_data` in `setparent`...
r48874 {"drop_merge_data", (PyCFunction)dirstate_item_drop_merge_data, METH_NOARGS,
"remove all \"merge-only\" from a DirstateItem"},
dirstate-entry: introduce dedicated accessors for v1 serialization...
r48298 {NULL} /* Sentinel */
};
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
dirstate-entry: add a `mode` property...
r48325 {
dirstate-item: introduce low level C function...
r48759 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
dirstate-entry: add a `mode` property...
r48325 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
dirstate-entry: add a `size` property...
r48326 {
dirstate-item: introduce low level C function...
r48759 return PyInt_FromLong(dirstate_item_c_v1_size(self));
dirstate-entry: add a `size` property...
r48326 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
dirstate-entry: add a `mtime` property...
r48327 {
dirstate-item: introduce low level C function...
r48759 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
dirstate-entry: add a `mtime` property...
r48327 };
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
dirstate-entry: add a `state` property (and use it)...
r48301 {
dirstate-item: introduce low level C function...
r48759 char state = dirstate_item_c_v1_state(self);
return PyBytes_FromStringAndSize(&state, 1);
dirstate-entry: add a `state` property (and use it)...
r48301 };
dirstate: add a concept of "fallback" flags to dirstate item...
r49068 static PyObject *dirstate_item_get_has_fallback_exec(dirstateItemObject *self)
{
if (dirstate_item_c_has_fallback_exec(self)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
static PyObject *dirstate_item_get_fallback_exec(dirstateItemObject *self)
{
if (dirstate_item_c_has_fallback_exec(self)) {
if (self->flags & dirstate_flag_fallback_exec) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
} else {
Py_RETURN_NONE;
}
};
static int dirstate_item_set_fallback_exec(dirstateItemObject *self,
PyObject *value)
{
if ((value == Py_None) || (value == NULL)) {
self->flags &= ~dirstate_flag_has_fallback_exec;
} else {
self->flags |= dirstate_flag_has_fallback_exec;
if (PyObject_IsTrue(value)) {
self->flags |= dirstate_flag_fallback_exec;
} else {
self->flags &= ~dirstate_flag_fallback_exec;
}
}
return 0;
};
static PyObject *
dirstate_item_get_has_fallback_symlink(dirstateItemObject *self)
{
if (dirstate_item_c_has_fallback_symlink(self)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
static PyObject *dirstate_item_get_fallback_symlink(dirstateItemObject *self)
{
if (dirstate_item_c_has_fallback_symlink(self)) {
if (self->flags & dirstate_flag_fallback_symlink) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
} else {
Py_RETURN_NONE;
}
};
static int dirstate_item_set_fallback_symlink(dirstateItemObject *self,
PyObject *value)
{
if ((value == Py_None) || (value == NULL)) {
self->flags &= ~dirstate_flag_has_fallback_symlink;
} else {
self->flags |= dirstate_flag_has_fallback_symlink;
if (PyObject_IsTrue(value)) {
self->flags |= dirstate_flag_fallback_symlink;
} else {
self->flags &= ~dirstate_flag_fallback_symlink;
}
}
return 0;
};
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
dirstate-entry: add a `tracked` property...
r48320 {
dirstate-item: introduce low level C function...
r48759 if (dirstate_item_c_tracked(self)) {
dirstate-entry: add a `tracked` property...
r48320 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: introduce a `p1_tracked` property...
r48955 static PyObject *dirstate_item_get_p1_tracked(dirstateItemObject *self)
{
if (self->flags & dirstate_flag_p1_tracked) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-entry: add a `tracked` property...
r48320
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
dirstate-entry: add a `added` property...
r48315 {
dirstate-item: introduce low level C function...
r48759 if (dirstate_item_c_added(self)) {
dirstate-entry: add a `added` property...
r48315 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: introduce a `p2_info` property that combine two others...
r48954 static PyObject *dirstate_item_get_p2_info(dirstateItemObject *self)
{
if (self->flags & dirstate_flag_wc_tracked &&
self->flags & dirstate_flag_p2_info) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
dirstate-entry: add a `merged` property...
r48302 {
dirstate-item: introduce low level C function...
r48759 if (dirstate_item_c_merged(self)) {
dirstate-entry: add a `merged` property...
r48302 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
dirstate-entry: add a `from_p2` property...
r48303 {
dirstate-item: introduce low level C function...
r48759 if (dirstate_item_c_from_p2(self)) {
dirstate-entry: add a `from_p2` property...
r48303 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: introduce a `maybe_clean` property...
r48898 static PyObject *dirstate_item_get_maybe_clean(dirstateItemObject *self)
{
if (!(self->flags & dirstate_flag_wc_tracked)) {
Py_RETURN_FALSE;
dirstate-item: change the internal storage and constructor value...
r48950 } else if (!(self->flags & dirstate_flag_p1_tracked)) {
dirstate-item: introduce a `maybe_clean` property...
r48898 Py_RETURN_FALSE;
dirstate-item: change the internal storage and constructor value...
r48950 } else if (self->flags & dirstate_flag_p2_info) {
dirstate-item: introduce a `maybe_clean` property...
r48898 Py_RETURN_FALSE;
} else {
Py_RETURN_TRUE;
}
};
dirstate-item: introduce a `any_tracked` property...
r48899 static PyObject *dirstate_item_get_any_tracked(dirstateItemObject *self)
{
dirstate-item: change the internal storage and constructor value...
r48950 if (dirstate_item_c_any_tracked(self)) {
dirstate-item: introduce a `any_tracked` property...
r48899 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: rename the class to DirstateItem...
r48328 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
dirstate-entry: add a `removed` property...
r48304 {
dirstate-item: introduce low level C function...
r48759 if (dirstate_item_c_removed(self)) {
dirstate-entry: add a `removed` property...
r48304 Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
};
dirstate-item: rename the class to DirstateItem...
r48328 static PyGetSetDef dirstate_item_getset[] = {
{"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
{"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
{"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
{"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
dirstate: add a concept of "fallback" flags to dirstate item...
r49068 {"has_fallback_exec", (getter)dirstate_item_get_has_fallback_exec, NULL,
"has_fallback_exec", NULL},
{"fallback_exec", (getter)dirstate_item_get_fallback_exec,
(setter)dirstate_item_set_fallback_exec, "fallback_exec", NULL},
{"has_fallback_symlink", (getter)dirstate_item_get_has_fallback_symlink,
NULL, "has_fallback_symlink", NULL},
{"fallback_symlink", (getter)dirstate_item_get_fallback_symlink,
(setter)dirstate_item_set_fallback_symlink, "fallback_symlink", NULL},
dirstate-item: rename the class to DirstateItem...
r48328 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
dirstate-item: introduce a `p1_tracked` property...
r48955 {"p1_tracked", (getter)dirstate_item_get_p1_tracked, NULL, "p1_tracked",
NULL},
dirstate-item: rename the class to DirstateItem...
r48328 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
dirstate-item: introduce a `p2_info` property that combine two others...
r48954 {"p2_info", (getter)dirstate_item_get_p2_info, NULL, "p2_info", NULL},
dirstate-item: rename the class to DirstateItem...
r48328 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
{"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
dirstate-item: introduce a `maybe_clean` property...
r48898 {"maybe_clean", (getter)dirstate_item_get_maybe_clean, NULL, "maybe_clean",
NULL},
dirstate-item: introduce a `any_tracked` property...
r48899 {"any_tracked", (getter)dirstate_item_get_any_tracked, NULL, "any_tracked",
NULL},
dirstate-item: rename the class to DirstateItem...
r48328 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
dirstate-entry: add a `state` property (and use it)...
r48301 {NULL} /* Sentinel */
};
dirstate-item: rename the class to DirstateItem...
r48328 PyTypeObject dirstateItemType = {
PyVarObject_HEAD_INIT(NULL, 0) /* header */
"dirstate_tuple", /* tp_name */
sizeof(dirstateItemObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)dirstate_item_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
dirstate-item: drop the deprecated __getitem__ variante...
r48737 0, /* tp_as_sequence */
dirstate-item: rename the class to DirstateItem...
r48328 0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"dirstate tuple", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
dirstate_item_methods, /* tp_methods */
0, /* tp_members */
dirstate_item_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
dirstate_item_new, /* tp_new */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
static PyObject *parse_dirstate(PyObject *self, PyObject *args)
{
PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
PyObject *fname = NULL, *cname = NULL, *entry = NULL;
char state, *cur, *str, *cpos;
int mode, size, mtime;
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 unsigned int flen, pos = 40;
Py_ssize_t len = 40;
Py_ssize_t readlen;
Yuya Nishihara
parsers: switch to policy importer...
r32372
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(
args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
len = readlen;
/* read parents */
if (len < 40) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_SetString(PyExc_ValueError,
"too little data for parents");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
Yuya Nishihara
cext: cast s# arguments of Py_BuildValue() to Py_ssize_t...
r42265 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
str + 20, (Py_ssize_t)20);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!parents) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
/* read filenames */
while (pos >= 40 && pos < len) {
if (pos + 17 > len) {
PyErr_SetString(PyExc_ValueError,
Augie Fackler
parsers: allow clang-format here...
r34863 "overflow in dirstate");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
cur = str + pos;
/* unpack header */
state = *cur;
mode = getbe32(cur + 1);
size = getbe32(cur + 5);
mtime = getbe32(cur + 9);
flen = getbe32(cur + 13);
pos += 17;
cur += 17;
if (flen > len - pos) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_SetString(PyExc_ValueError,
"overflow in dirstate");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
}
dirstate-item: add a `from_v1_data` constructor...
r48465 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
size, mtime);
Yuya Nishihara
dirstate: fix parse_dirstate() to error out if NULL entry created...
r48839 if (!entry)
goto quit;
Yuya Nishihara
parsers: switch to policy importer...
r32372 cpos = memchr(cur, 0, flen);
if (cpos) {
fname = PyBytes_FromStringAndSize(cur, cpos - cur);
Augie Fackler
parsers: allow clang-format here...
r34863 cname = PyBytes_FromStringAndSize(
cpos + 1, flen - (cpos - cur) - 1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!fname || !cname ||
PyDict_SetItem(cmap, fname, cname) == -1 ||
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 PyDict_SetItem(dmap, fname, entry) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(cname);
} else {
fname = PyBytes_FromStringAndSize(cur, flen);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!fname ||
PyDict_SetItem(dmap, fname, entry) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto quit;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
Py_DECREF(fname);
Py_DECREF(entry);
fname = cname = entry = NULL;
pos += flen;
}
ret = parents;
Py_INCREF(ret);
quit:
Py_XDECREF(fname);
Py_XDECREF(cname);
Py_XDECREF(entry);
Py_XDECREF(parents);
return ret;
}
/*
* Efficiently pack a dirstate object into its on-disk format.
*/
static PyObject *pack_dirstate(PyObject *self, PyObject *args)
{
PyObject *packobj = NULL;
PyObject *map, *copymap, *pl, *mtime_unset = NULL;
Py_ssize_t nbytes, pos, l;
PyObject *k, *v = NULL, *pn;
char *p, *s;
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 int now_s;
int now_ns;
Yuya Nishihara
parsers: switch to policy importer...
r32372
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 if (!PyArg_ParseTuple(args, "O!O!O!(ii):pack_dirstate", &PyDict_Type,
&map, &PyDict_Type, &copymap, &PyTuple_Type, &pl,
&now_s, &now_ns)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 if (PyTuple_Size(pl) != 2) {
PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
}
/* Figure out how much we need to allocate. */
for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
PyObject *c;
if (!PyBytes_Check(k)) {
PyErr_SetString(PyExc_TypeError, "expected string key");
goto bail;
}
nbytes += PyBytes_GET_SIZE(k) + 17;
c = PyDict_GetItem(copymap, k);
if (c) {
if (!PyBytes_Check(c)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected string key");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
nbytes += PyBytes_GET_SIZE(c) + 1;
}
}
packobj = PyBytes_FromStringAndSize(NULL, nbytes);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (packobj == NULL) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
p = PyBytes_AS_STRING(packobj);
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 pn = PyTuple_GET_ITEM(pl, 0);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
goto bail;
}
memcpy(p, s, l);
p += 20;
Yuya Nishihara
dirstate: use tuple interface to fix leak in pack_dirstate()...
r39485 pn = PyTuple_GET_ITEM(pl, 1);
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
goto bail;
}
memcpy(p, s, l);
p += 20;
Augie Fackler
parsers: allow clang-format here...
r34863 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
dirstate-item: rename the class to DirstateItem...
r48328 dirstateItemObject *tuple;
Yuya Nishihara
parsers: switch to policy importer...
r32372 char state;
int mode, size, mtime;
Py_ssize_t len, l;
PyObject *o;
char *t;
if (!dirstate_tuple_check(v)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
parsers: allow clang-format here...
r34863 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
dirstate-item: rename the class to DirstateItem...
r48328 tuple = (dirstateItemObject *)v;
Yuya Nishihara
parsers: switch to policy importer...
r32372
dirstate-item: move the C implementation to the same logic...
r48760 state = dirstate_item_c_v1_state(tuple);
mode = dirstate_item_c_v1_mode(tuple);
size = dirstate_item_c_v1_size(tuple);
mtime = dirstate_item_c_v1_mtime(tuple);
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 if (state == 'n' && tuple->mtime_s == now_s) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 /* See pure/parsers.py:pack_dirstate for why we do
* this. */
mtime = -1;
dirstate-item: `dirstate_item_from_v1_data` replaces make_dirstate_item...
r48757 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
Augie Fackler
parsers: allow clang-format here...
r34863 state, mode, size, mtime);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!mtime_unset) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
if (PyDict_SetItem(map, k, mtime_unset) == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 Py_DECREF(mtime_unset);
mtime_unset = NULL;
}
*p++ = state;
putbe32((uint32_t)mode, p);
putbe32((uint32_t)size, p + 4);
putbe32((uint32_t)mtime, p + 8);
t = p + 12;
p += 16;
len = PyBytes_GET_SIZE(k);
memcpy(p, PyBytes_AS_STRING(k), len);
p += len;
o = PyDict_GetItem(copymap, k);
if (o) {
*p++ = '\0';
l = PyBytes_GET_SIZE(o);
memcpy(p, PyBytes_AS_STRING(o), l);
p += l;
len += l + 1;
}
putbe32((uint32_t)len, t);
}
pos = p - PyBytes_AS_STRING(packobj);
if (pos != nbytes) {
PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
Augie Fackler
parsers: allow clang-format here...
r34863 (long)pos, (long)nbytes);
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
return packobj;
bail:
Py_XDECREF(mtime_unset);
Py_XDECREF(packobj);
Py_XDECREF(v);
return NULL;
}
#define BUMPED_FIX 1
#define USING_SHA_256 2
#define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
Augie Fackler
parsers: allow clang-format here...
r34863 static PyObject *readshas(const char *source, unsigned char num,
Py_ssize_t hashwidth)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
int i;
PyObject *list = PyTuple_New(num);
if (list == NULL) {
return NULL;
}
for (i = 0; i < num; i++) {
PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
if (hash == NULL) {
Py_DECREF(list);
return NULL;
}
PyTuple_SET_ITEM(list, i, hash);
source += hashwidth;
}
return list;
}
static PyObject *fm1readmarker(const char *databegin, const char *dataend,
Augie Fackler
parsers: allow clang-format here...
r34863 uint32_t *msize)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
const char *data = databegin;
const char *meta;
double mtime;
int16_t tz;
uint16_t flags;
unsigned char nsuccs, nparents, nmetadata;
Py_ssize_t hashwidth = 20;
PyObject *prec = NULL, *parents = NULL, *succs = NULL;
PyObject *metadata = NULL, *ret = NULL;
int i;
if (data + FM1_HEADER_SIZE > dataend) {
goto overflow;
}
*msize = getbe32(data);
data += 4;
mtime = getbefloat64(data);
data += 8;
tz = getbeint16(data);
data += 2;
flags = getbeuint16(data);
data += 2;
if (flags & USING_SHA_256) {
hashwidth = 32;
}
nsuccs = (unsigned char)(*data++);
nparents = (unsigned char)(*data++);
nmetadata = (unsigned char)(*data++);
if (databegin + *msize > dataend) {
goto overflow;
}
Augie Fackler
parsers: allow clang-format here...
r34863 dataend = databegin + *msize; /* narrow down to marker size */
Yuya Nishihara
parsers: switch to policy importer...
r32372
if (data + hashwidth > dataend) {
goto overflow;
}
prec = PyBytes_FromStringAndSize(data, hashwidth);
data += hashwidth;
if (prec == NULL) {
goto bail;
}
if (data + nsuccs * hashwidth > dataend) {
goto overflow;
}
succs = readshas(data, nsuccs, hashwidth);
if (succs == NULL) {
goto bail;
}
data += nsuccs * hashwidth;
if (nparents == 1 || nparents == 2) {
if (data + nparents * hashwidth > dataend) {
goto overflow;
}
parents = readshas(data, nparents, hashwidth);
if (parents == NULL) {
goto bail;
}
data += nparents * hashwidth;
} else {
parents = Py_None;
Py_INCREF(parents);
}
if (data + 2 * nmetadata > dataend) {
goto overflow;
}
meta = data + (2 * nmetadata);
metadata = PyTuple_New(nmetadata);
if (metadata == NULL) {
goto bail;
}
for (i = 0; i < nmetadata; i++) {
PyObject *tmp, *left = NULL, *right = NULL;
Py_ssize_t leftsize = (unsigned char)(*data++);
Py_ssize_t rightsize = (unsigned char)(*data++);
if (meta + leftsize + rightsize > dataend) {
goto overflow;
}
left = PyBytes_FromStringAndSize(meta, leftsize);
meta += leftsize;
right = PyBytes_FromStringAndSize(meta, rightsize);
meta += rightsize;
tmp = PyTuple_New(2);
if (!left || !right || !tmp) {
Py_XDECREF(left);
Py_XDECREF(right);
Py_XDECREF(tmp);
goto bail;
}
PyTuple_SET_ITEM(tmp, 0, left);
PyTuple_SET_ITEM(tmp, 1, right);
PyTuple_SET_ITEM(metadata, i, tmp);
}
Augie Fackler
parsers: allow clang-format here...
r34863 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
(int)tz * 60, parents);
goto bail; /* return successfully */
Yuya Nishihara
parsers: switch to policy importer...
r32372
overflow:
PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
bail:
Py_XDECREF(prec);
Py_XDECREF(succs);
Py_XDECREF(metadata);
Py_XDECREF(parents);
return ret;
}
Gregory Szorc
cext: wrap before brace for functions...
r34441 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
{
Yuya Nishihara
parsers: switch to policy importer...
r32372 const char *data, *dataend;
Gregory Szorc
cext: make parsers.c PY_SSIZE_T_CLEAN...
r42235 Py_ssize_t datalen, offset, stop;
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyObject *markers = NULL;
Yuya Nishihara
py3: bulk-replace 'const char*' format specifier passed to PyArg_ParseTuple*()...
r36638 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
&offset, &stop)) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return NULL;
}
Augie Fackler
parsers: better bounds checking in fm1readmarkers...
r41052 if (offset < 0) {
PyErr_SetString(PyExc_ValueError,
"invalid negative offset in fm1readmarkers");
return NULL;
}
if (stop > datalen) {
PyErr_SetString(
PyExc_ValueError,
"stop longer than data length in fm1readmarkers");
return NULL;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372 dataend = data + datalen;
data += offset;
markers = PyList_New(0);
if (!markers) {
return NULL;
}
while (offset < stop) {
uint32_t msize;
int error;
PyObject *record = fm1readmarker(data, dataend, &msize);
if (!record) {
goto bail;
}
error = PyList_Append(markers, record);
Py_DECREF(record);
if (error) {
goto bail;
}
data += msize;
offset += msize;
}
return markers;
bail:
Py_DECREF(markers);
return NULL;
}
static char parsers_doc[] = "Efficient content parsing.";
PyObject *encodedir(PyObject *self, PyObject *args);
PyObject *pathencode(PyObject *self, PyObject *args);
PyObject *lowerencode(PyObject *self, PyObject *args);
Raphaël Gomès
cext: add support for revlogv2...
r47442 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
Yuya Nishihara
parsers: switch to policy importer...
r32372
static PyMethodDef methods[] = {
Augie Fackler
parsers: allow clang-format here...
r34863 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
{"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
Raphaël Gomès
cext: add support for revlogv2...
r47442 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
"parse a revlog index\n"},
Augie Fackler
parsers: allow clang-format here...
r34863 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
{"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
{"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
{"dict_new_presized", dict_new_presized, METH_VARARGS,
"construct a dict with an expected size\n"},
{"make_file_foldmap", make_file_foldmap, METH_VARARGS,
"make file foldmap\n"},
{"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
"escape a UTF-8 byte string to JSON (fast path)\n"},
{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
{"fm1readmarkers", fm1readmarkers, METH_VARARGS,
"parse v1 obsolete markers\n"},
{NULL, NULL}};
Yuya Nishihara
parsers: switch to policy importer...
r32372
void dirs_module_init(PyObject *mod);
void manifest_module_init(PyObject *mod);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 void revlog_module_init(PyObject *mod);
Yuya Nishihara
parsers: switch to policy importer...
r32372
revlog: signal which revlog index are compatible with Rust...
r48042 static const int version = 20;
Yuya Nishihara
parsers: switch to policy importer...
r32372
static void module_init(PyObject *mod)
{
PyModule_AddIntConstant(mod, "version", version);
/* This module constant has two purposes. First, it lets us unit test
* the ImportError raised without hard-coding any error text. This
* means we can change the text in the future without breaking tests,
* even across changesets without a recompile. Second, its presence
* can be used to determine whether the version-checking logic is
* present, which also helps in testing across changesets without a
* recompile. Note that this means the pure-Python version of parsers
* should not have this module constant. */
PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
dirs_module_init(mod);
manifest_module_init(mod);
Gregory Szorc
cext: extract revlog/index parsing code to own C file...
r32378 revlog_module_init(mod);
Yuya Nishihara
parsers: switch to policy importer...
r32372
dirstate-item: rename the class to DirstateItem...
r48328 if (PyType_Ready(&dirstateItemType) < 0) {
Yuya Nishihara
cext: move back finalization of dirstateTupleType where it should be
r32383 return;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
dirstate-item: rename the class to DirstateItem...
r48328 Py_INCREF(&dirstateItemType);
PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
Yuya Nishihara
parsers: switch to policy importer...
r32372 }
static int check_python_version(void)
{
PyObject *sys = PyImport_ImportModule("sys"), *ver;
long hexversion;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!sys) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 ver = PyObject_GetAttrString(sys, "hexversion");
Py_DECREF(sys);
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (!ver) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 hexversion = PyInt_AsLong(ver);
Py_DECREF(ver);
/* sys.hexversion is a 32-bit number by default, so the -1 case
* should only occur in unusual circumstances (e.g. if sys.hexversion
* is manually set to an invalid value). */
if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
Augie Fackler
parsers: allow clang-format here...
r34863 PyErr_Format(PyExc_ImportError,
"%s: The Mercurial extension "
"modules were compiled with Python " PY_VERSION
", but "
"Mercurial is currently using Python with "
"sys.hexversion=%ld: "
"Python %s\n at: %s",
versionerrortext, hexversion, Py_GetVersion(),
Py_GetProgramFullPath());
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
return 0;
}
#ifdef IS_PY3K
Augie Fackler
parsers: allow clang-format here...
r34863 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
parsers_doc, -1, methods};
Yuya Nishihara
parsers: switch to policy importer...
r32372
PyMODINIT_FUNC PyInit_parsers(void)
{
PyObject *mod;
if (check_python_version() == -1)
return NULL;
mod = PyModule_Create(&parsers_module);
module_init(mod);
return mod;
}
#else
PyMODINIT_FUNC initparsers(void)
{
PyObject *mod;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 if (check_python_version() == -1) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 return;
Augie Fackler
cleanup: use clang-tidy to add missing {} around one-line statements...
r41367 }
Yuya Nishihara
parsers: switch to policy importer...
r32372 mod = Py_InitModule3("parsers", methods, parsers_doc);
module_init(mod);
}
#endif