| #include "Python.h" |
| #include "pycore_pymem.h" // PYMEM_DEADBYTE |
| |
| #include "source.h" |
| |
| #define SOURCE_BUFFER_GROWTH_FACTOR 2 |
| |
| void |
| _PyTok_SourceInit(_PyTok_SourceText *source) |
| { |
| *source = (_PyTok_SourceText){0}; |
| } |
| |
| void |
| _PyTok_SourceClear(_PyTok_SourceText *source) |
| { |
| PyMem_Free(source->bytes); |
| _PyTok_SourceInit(source); |
| } |
| |
| void |
| _PyTok_SourceDiscard(_PyTok_SourceText *source) |
| { |
| assert(source->base_offset <= PY_SSIZE_T_MAX - source->len); |
| source->base_offset += source->len; |
| source->len = 0; |
| if (source->bytes != NULL) { |
| source->bytes[0] = '\0'; |
| } |
| source->nlines = 0; |
| } |
| |
| static int |
| reserve_bytes(_PyTok_SourceText *source, Py_ssize_t needed) |
| { |
| if (needed <= source->cap) { |
| return 0; |
| } |
| Py_ssize_t cap = source->cap > 0 ? source->cap : BUFSIZ; |
| while (cap < needed) { |
| if (cap > PY_SSIZE_T_MAX / SOURCE_BUFFER_GROWTH_FACTOR) { |
| cap = needed; |
| break; |
| } |
| cap *= SOURCE_BUFFER_GROWTH_FACTOR; |
| } |
| char *bytes; |
| #ifdef Py_DEBUG |
| /* Moving on growth makes stale interior pointers fail in debug builds. */ |
| bytes = PyMem_Malloc(cap); |
| if (bytes != NULL && source->len > 0) { |
| memcpy(bytes, source->bytes, source->len); |
| } |
| #else |
| bytes = PyMem_Realloc(source->bytes, cap); |
| #endif |
| if (bytes == NULL) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| #ifdef Py_DEBUG |
| if (source->bytes != NULL) { |
| memset(source->bytes, PYMEM_DEADBYTE, source->cap); |
| PyMem_Free(source->bytes); |
| } |
| #endif |
| source->bytes = bytes; |
| source->cap = cap; |
| return 0; |
| } |
| |
| static int |
| validate_line(const _PyTok_SourceText *source, const char *bytes, |
| Py_ssize_t len, int append_newline) |
| { |
| if (len < 0 || (len == 0 && !append_newline) || bytes == NULL || |
| (source->nlines > 0 && |
| (source->len == 0 || source->bytes[source->len - 1] != '\n'))) { |
| PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line"); |
| return -1; |
| } |
| const char *newline = memchr(bytes, '\n', len); |
| if (newline != NULL && (append_newline || newline != bytes + len - 1)) { |
| PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line"); |
| return -1; |
| } |
| if (source->nlines == INT_MAX || |
| (source->nlines == INT_MAX - 1 && (newline != NULL || append_newline))) { |
| PyErr_SetString(PyExc_OverflowError, "too many tokenizer source lines"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static _PyTok_Off |
| append_line(_PyTok_SourceText *source, const char *bytes, |
| Py_ssize_t len, int append_newline) |
| { |
| if (validate_line(source, bytes, len, append_newline) < 0) { |
| return -1; |
| } |
| if (source->len > PY_SSIZE_T_MAX - len - append_newline - 1 || |
| source->base_offset > PY_SSIZE_T_MAX - source->len - len - append_newline) { |
| PyErr_NoMemory(); |
| return -1; |
| } |
| if (reserve_bytes(source, source->len + len + append_newline + 1) < 0) { |
| return -1; |
| } |
| |
| _PyTok_Off start = source->len; |
| memcpy(source->bytes + start, bytes, len); |
| source->len += len; |
| if (append_newline) { |
| source->bytes[source->len++] = '\n'; |
| } |
| source->bytes[source->len] = '\0'; |
| source->nlines++; |
| return source->base_offset + start; |
| } |
| |
| _PyTok_Off |
| _PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes, |
| Py_ssize_t len) |
| { |
| return append_line(source, bytes, len, 0); |
| } |
| |
| _PyTok_Off |
| _PyTok_SourceAppendLineWithNewline(_PyTok_SourceText *source, const char *bytes, |
| Py_ssize_t len) |
| { |
| return append_line(source, bytes, len, 1); |
| } |
| |
| const char * |
| _PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno, |
| Py_ssize_t *len) |
| { |
| assert(len != NULL); |
| const char *line = _PyTok_SourceData(source); |
| const char *end = line + source->len; |
| while (lineno > 1) { |
| const char *newline = memchr(line, '\n', end - line); |
| if (newline == NULL) { |
| break; |
| } |
| line = newline + 1; |
| lineno--; |
| } |
| const char *newline = memchr(line, '\n', end - line); |
| *len = (newline != NULL ? newline : end) - line; |
| return line; |
| } |