| /* Parse tree node implementation */ | 
 |  | 
 | #include "Python.h" | 
 | #include "node.h" | 
 | #include "errcode.h" | 
 |  | 
 | node * | 
 | PyNode_New(int type) | 
 | { | 
 |     node *n = (node *) PyObject_MALLOC(1 * sizeof(node)); | 
 |     if (n == NULL) | 
 |         return NULL; | 
 |     n->n_type = type; | 
 |     n->n_str = NULL; | 
 |     n->n_lineno = 0; | 
 |     n->n_end_lineno = 0; | 
 |     n->n_end_col_offset = -1; | 
 |     n->n_nchildren = 0; | 
 |     n->n_child = NULL; | 
 |     return n; | 
 | } | 
 |  | 
 | /* See comments at XXXROUNDUP below.  Returns -1 on overflow. */ | 
 | static int | 
 | fancy_roundup(int n) | 
 | { | 
 |     /* Round up to the closest power of 2 >= n. */ | 
 |     int result = 256; | 
 |     assert(n > 128); | 
 |     while (result < n) { | 
 |         result <<= 1; | 
 |         if (result <= 0) | 
 |             return -1; | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | /* A gimmick to make massive numbers of reallocs quicker.  The result is | 
 |  * a number >= the input.  In PyNode_AddChild, it's used like so, when | 
 |  * we're about to add child number current_size + 1: | 
 |  * | 
 |  *     if XXXROUNDUP(current_size) < XXXROUNDUP(current_size + 1): | 
 |  *         allocate space for XXXROUNDUP(current_size + 1) total children | 
 |  *     else: | 
 |  *         we already have enough space | 
 |  * | 
 |  * Since a node starts out empty, we must have | 
 |  * | 
 |  *     XXXROUNDUP(0) < XXXROUNDUP(1) | 
 |  * | 
 |  * so that we allocate space for the first child.  One-child nodes are very | 
 |  * common (presumably that would change if we used a more abstract form | 
 |  * of syntax tree), so to avoid wasting memory it's desirable that | 
 |  * XXXROUNDUP(1) == 1.  That in turn forces XXXROUNDUP(0) == 0. | 
 |  * | 
 |  * Else for 2 <= n <= 128, we round up to the closest multiple of 4.  Why 4? | 
 |  * Rounding up to a multiple of an exact power of 2 is very efficient, and | 
 |  * most nodes with more than one child have <= 4 kids. | 
 |  * | 
 |  * Else we call fancy_roundup() to grow proportionately to n.  We've got an | 
 |  * extreme case then (like test_longexp.py), and on many platforms doing | 
 |  * anything less than proportional growth leads to exorbitant runtime | 
 |  * (e.g., MacPython), or extreme fragmentation of user address space (e.g., | 
 |  * Win98). | 
 |  * | 
 |  * In a run of compileall across the 2.3a0 Lib directory, Andrew MacIntyre | 
 |  * reported that, with this scheme, 89% of PyObject_REALLOC calls in | 
 |  * PyNode_AddChild passed 1 for the size, and 9% passed 4.  So this usually | 
 |  * wastes very little memory, but is very effective at sidestepping | 
 |  * platform-realloc disasters on vulnerable platforms. | 
 |  * | 
 |  * Note that this would be straightforward if a node stored its current | 
 |  * capacity.  The code is tricky to avoid that. | 
 |  */ | 
 | #define XXXROUNDUP(n) ((n) <= 1 ? (n) :                         \ | 
 |                (n) <= 128 ? (int)_Py_SIZE_ROUND_UP((n), 4) :    \ | 
 |                fancy_roundup(n)) | 
 |  | 
 |  | 
 | void | 
 | _PyNode_FinalizeEndPos(node *n) | 
 | { | 
 |     int nch = NCH(n); | 
 |     node *last; | 
 |     if (nch == 0) { | 
 |         return; | 
 |     } | 
 |     last = CHILD(n, nch - 1); | 
 |     _PyNode_FinalizeEndPos(last); | 
 |     n->n_end_lineno = last->n_end_lineno; | 
 |     n->n_end_col_offset = last->n_end_col_offset; | 
 | } | 
 |  | 
 | int | 
 | PyNode_AddChild(node *n1, int type, char *str, int lineno, int col_offset, | 
 |                 int end_lineno, int end_col_offset) | 
 | { | 
 |     const int nch = n1->n_nchildren; | 
 |     int current_capacity; | 
 |     int required_capacity; | 
 |     node *n; | 
 |  | 
 |     // finalize end position of previous node (if any) | 
 |     if (nch > 0) { | 
 |         _PyNode_FinalizeEndPos(CHILD(n1, nch - 1)); | 
 |     } | 
 |  | 
 |     if (nch == INT_MAX || nch < 0) | 
 |         return E_OVERFLOW; | 
 |  | 
 |     current_capacity = XXXROUNDUP(nch); | 
 |     required_capacity = XXXROUNDUP(nch + 1); | 
 |     if (current_capacity < 0 || required_capacity < 0) | 
 |         return E_OVERFLOW; | 
 |     if (current_capacity < required_capacity) { | 
 |         if ((size_t)required_capacity > SIZE_MAX / sizeof(node)) { | 
 |             return E_NOMEM; | 
 |         } | 
 |         n = n1->n_child; | 
 |         n = (node *) PyObject_REALLOC(n, | 
 |                                       required_capacity * sizeof(node)); | 
 |         if (n == NULL) | 
 |             return E_NOMEM; | 
 |         n1->n_child = n; | 
 |     } | 
 |  | 
 |     n = &n1->n_child[n1->n_nchildren++]; | 
 |     n->n_type = type; | 
 |     n->n_str = str; | 
 |     n->n_lineno = lineno; | 
 |     n->n_col_offset = col_offset; | 
 |     n->n_end_lineno = end_lineno;  // this and below will be updates after all children are added. | 
 |     n->n_end_col_offset = end_col_offset; | 
 |     n->n_nchildren = 0; | 
 |     n->n_child = NULL; | 
 |     return 0; | 
 | } | 
 |  | 
 | /* Forward */ | 
 | static void freechildren(node *); | 
 | static Py_ssize_t sizeofchildren(node *n); | 
 |  | 
 |  | 
 | void | 
 | PyNode_Free(node *n) | 
 | { | 
 |     if (n != NULL) { | 
 |         freechildren(n); | 
 |         PyObject_FREE(n); | 
 |     } | 
 | } | 
 |  | 
 | Py_ssize_t | 
 | _PyNode_SizeOf(node *n) | 
 | { | 
 |     Py_ssize_t res = 0; | 
 |  | 
 |     if (n != NULL) | 
 |         res = sizeof(node) + sizeofchildren(n); | 
 |     return res; | 
 | } | 
 |  | 
 | static void | 
 | freechildren(node *n) | 
 | { | 
 |     int i; | 
 |     for (i = NCH(n); --i >= 0; ) | 
 |         freechildren(CHILD(n, i)); | 
 |     if (n->n_child != NULL) | 
 |         PyObject_FREE(n->n_child); | 
 |     if (STR(n) != NULL) | 
 |         PyObject_FREE(STR(n)); | 
 | } | 
 |  | 
 | static Py_ssize_t | 
 | sizeofchildren(node *n) | 
 | { | 
 |     Py_ssize_t res = 0; | 
 |     int i; | 
 |     for (i = NCH(n); --i >= 0; ) | 
 |         res += sizeofchildren(CHILD(n, i)); | 
 |     if (n->n_child != NULL) | 
 |         /* allocated size of n->n_child array */ | 
 |         res += XXXROUNDUP(NCH(n)) * sizeof(node); | 
 |     if (STR(n) != NULL) | 
 |         res += strlen(STR(n)) + 1; | 
 |     return res; | 
 | } |