[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/atpalmer-python/python-cstring/master/src/cstring.c [Back]  [Original]

#include 

#define WHITESPACE_CHARS    " \t\n\v\f\r"

/* memrchr not available on some systems, so reimplement. */
const char *_memrchr(const char *s, int c, size_t n) {
    for(const char *p = s + n - 1; p >= s; --p) {
        if(*p == c)
            return p;
    }
    return NULL;
}

const char *_strrstr(const char *s, const char *find) {
    const char *p = s + strlen(s) - 1;
    for(;p > s; --p) {
        if(memcmp(p, find, strlen(find)) == 0)
            return p;
    }
    return NULL;
}


struct cstring {
    PyObject_VAR_HEAD
    Py_hash_t hash;
    char value[];
};

static PyTypeObject cstring_type;

#define CSTRING_HASH(self)          (((struct cstring *)self)->hash)
#define CSTRING_VALUE(self)         (((struct cstring *)self)->value)
#define CSTRING_VALUE_AT(self, i)   (&CSTRING_VALUE(self)[(i)])
#define CSTRING_LAST_BYTE(self)     (CSTRING_VALUE(self)[Py_SIZE(self) - 1])

#define CSTRING_ALLOC(tp, len)      ((struct cstring *)(tp)->tp_alloc((tp), (len)))

/* singleton, initialized in cstring_new_empty */
static const struct cstring *cstring_EMPTY = NULL;

static void *_bad_argument_type(PyObject *o) {
    PyErr_Format(
        PyExc_TypeError,
        "Bad argument type: %s",
        Py_TYPE(o)->tp_name);
    return NULL;
}

static PyObject *_cstring_new(PyTypeObject *type, const char *value, Py_ssize_t len) {
    struct cstring *new = CSTRING_ALLOC(type, len + 1);
    if(!new)
        return NULL;
    new->hash = -1;
    memcpy(new->value, value, len);
    CSTRING_LAST_BYTE(new) = '\0';
    return (PyObject *)new;
}

static PyObject *_cstring_realloc(PyObject *self, Py_ssize_t len) {
    if(Py_REFCNT(self) > 1)
        return PyErr_BadInternalCall(), NULL;
    struct cstring *new = PyObject_Realloc(self, sizeof(struct cstring) + len + 1);
    if(!new)
        return PyErr_NoMemory();
    Py_SET_SIZE(new, len + 1);
    new->hash = -1;
    return (PyObject *)new;
}

static PyObject *_cstring_copy(PyObject *self) {
    return _cstring_new(Py_TYPE(self), CSTRING_VALUE(self), Py_SIZE(self) - 1);
}

static PyObject *cstring_new_empty(void) {
    if(!cstring_EMPTY) {
        cstring_EMPTY = (struct cstring *)_cstring_new(&cstring_type, "", 0);
    }
    /* leaking one reference for singleton cache (never cleaned up) */
    Py_INCREF(cstring_EMPTY);
    return (PyObject *)cstring_EMPTY;
}

static const char *_obj_as_string_and_size(PyObject *o, Py_ssize_t *s) {
    if(PyUnicode_Check(o))
        return PyUnicode_AsUTF8AndSize(o, s);

    if(PyObject_CheckBuffer(o)) {
        /* handles bytes, bytearrays, arrays, memoryviews, etc. */
        Py_buffer view;
        if(PyObject_GetBuffer(o, &view, PyBUF_SIMPLE) < 0)
            return NULL;
        *s = view.len;
        const char *buffer = view.buf;
        PyBuffer_Release(&view);
        return buffer;
    }

    if(PyObject_TypeCheck(o, &cstring_type)) {
        /* TODO: implement buffer protocol for cstring */
        *s = Py_SIZE(o) - 1;
        return CSTRING_VALUE(o);
    }

    *s = -1;
    return _bad_argument_type(o);
}

static PyObject *cstring_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
    PyObject *argobj = NULL;
    if(!PyArg_ParseTuple(args, "O", &argobj))
        return NULL;

    if(PyObject_TypeCheck(argobj, type)) {
        Py_INCREF(argobj);
        return argobj;
    }

    Py_ssize_t len = 0;
    const char *buffer = _obj_as_string_and_size(argobj, &len);
    if(!buffer)
        return NULL;

    if(len == 0)
        return cstring_new_empty();

    return _cstring_new(type, buffer, len);
}

static void cstring_dealloc(PyObject *self) {
    Py_TYPE(self)->tp_free(self);
}

static int _ensure_cstring(PyObject *self) {
    if(PyObject_TypeCheck(self, &cstring_type))
        return 1;
    PyErr_Format(
        PyExc_TypeError,
        "Object must have type cstring, not %s.",
        Py_TYPE(self)->tp_name);
    return 0;
}

static PyObject *cstring_str(PyObject *self) {
    return PyUnicode_FromString(CSTRING_VALUE(self));
}

static PyObject *cstring_repr(PyObject *self) {
    PyObject *tmp = cstring_str(self);
    PyObject *repr = PyObject_Repr(tmp);
    Py_DECREF(tmp);
    return repr;
}

static Py_hash_t cstring_hash(PyObject *self) {
    if(CSTRING_HASH(self) == -1)
        CSTRING_HASH(self) = _Py_HashBytes(CSTRING_VALUE(self), Py_SIZE(self));
    return CSTRING_HASH(self);
}

static PyObject *cstring_richcompare(PyObject *self, PyObject *other, int op) {
    if(!_ensure_cstring(other))
        return NULL;

    const char *left = CSTRING_VALUE(self);
    const char *right = CSTRING_VALUE(other);

    for(;*left && *right && *left == *right; ++left, ++right)
        ;

    switch (op) {
    case Py_EQ:
        return PyBool_FromLong(*left == *right);
    case Py_NE:
        return PyBool_FromLong(*left != *right);
    case Py_LT:
        return PyBool_FromLong(*left < *right);
    case Py_GT:
        return PyBool_FromLong(*left > *right);
    case Py_LE:
        return PyBool_FromLong(*left = *right);
    default:
        Py_UNREACHABLE();
    }
}

static Py_ssize_t cstring_len(PyObject *self) {
    return Py_SIZE(self) - 1;
}

static PyObject *_concat_in_place(PyObject *self, PyObject *other) {
    if(!other)
        return PyErr_BadArgument(), NULL;
    if(!_ensure_cstring(other))
        return NULL;
    if(!self)
        return _cstring_copy(other);  /* new (mutable) copy with refcnt=1 */
    if(!_ensure_cstring(self))
        return NULL;

    Py_ssize_t origlen = cstring_len(self);
    Py_ssize_t newlen = origlen + cstring_len(other);
    PyObject *new = _cstring_realloc(self, newlen);
    if(!new)
        return NULL;

    memcpy(CSTRING_VALUE_AT(new, origlen), CSTRING_VALUE(other), cstring_len(other));
    CSTRING_LAST_BYTE(new) = '\0';
    return new;
}

static PyObject *cstring_concat(PyObject *left, PyObject *right) {
    if(!_ensure_cstring(left))
        return NULL;
    if(!_ensure_cstring(right))
        return NULL;

    Py_ssize_t size = cstring_len(left) + cstring_len(right) + 1;

    struct cstring *new = CSTRING_ALLOC(Py_TYPE(left), size);
    if(!new)
        return NULL;
    memcpy(new->value, CSTRING_VALUE(left), Py_SIZE(left));
    memcpy(&new->value[cstring_len(left)], CSTRING_VALUE(right), Py_SIZE(right)); 
    return (PyObject *)new;
}

static PyObject *cstring_repeat(PyObject *self, Py_ssize_t count) {
    if(!_ensure_cstring(self))
        return NULL;
    if(count value[i], CSTRING_VALUE(self), Py_SIZE(self));
    }
    return (PyObject *)new;
}

static Py_ssize_t _ensure_valid_index(PyObject *self, Py_ssize_t i) {
    if(i >= 0 && i < cstring_len(self))
        return i;
    PyErr_SetString(PyExc_IndexError, "Index is out of bounds");
    return -1;
}

static PyObject *cstring_item(PyObject *self, Py_ssize_t i) {
    if(_ensure_valid_index(self, i) < 0)
        return NULL;
    return _cstring_new(Py_TYPE(self), CSTRING_VALUE_AT(self, i), 1);
}

static int cstring_contains(PyObject *self, PyObject *arg) {
    if(!_ensure_cstring(arg))
        return -1;
    if(strstr(CSTRING_VALUE(self), CSTRING_VALUE(arg)))
        return 1;
    return 0;
}

static PyObject *_cstring_subscript_index(PyObject *self, PyObject *index) {
    Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError);
    if(PyErr_Occurred())
        return NULL;
    if(i < 0)
        i += cstring_len(self);
    return cstring_item(self, i);
}

static PyObject *_cstring_subscript_slice(PyObject *self, PyObject *slice) {
    Py_ssize_t start, stop, step;
    if(PySlice_Unpack(slice, &start, &stop, &step) < 0)
        return NULL;

    Py_ssize_t slicelen = PySlice_AdjustIndices(cstring_len(self), &start, &stop, step);
    struct cstring *new = CSTRING_ALLOC(Py_TYPE(self), slicelen + 1);
    if(!new)
        return NULL;

    char *src = CSTRING_VALUE_AT(self, start);
    for(Py_ssize_t i = 0; i < slicelen; ++i) {
        new->value[i] = *src;
        src += step;
    }
    new->value[slicelen] = '\0';
    return (PyObject *)new;
}

static PyObject *cstring_subscript(PyObject *self, PyObject *key) {
    if(PyIndex_Check(key))
        return _cstring_subscript_index(self, key);
    if(PySlice_Check(key))
        return _cstring_subscript_slice(self, key);

    PyErr_SetString(PyExc_TypeError, "Subscript must be int or slice.");
    return NULL;
}

static Py_ssize_t _fix_index(Py_ssize_t i, Py_ssize_t len) {
    Py_ssize_t result = i;
    if(result < 0)
        result += len;
    if(result < 0)
        result = 0;
    if(result > len)
        result = len;
    return result;
}

struct _substr_params {
    const char *start;
    const char *end;
    const char *substr;
    Py_ssize_t substr_len;
};

static struct _substr_params *_parse_substr_args(PyObject *self, PyObject *args, struct _substr_params *params) {
    PyObject *substr_obj;
    Py_ssize_t start = 0;
    Py_ssize_t end = PY_SSIZE_T_MAX;

    if(!PyArg_ParseTuple(args, "O|nn", &substr_obj, &start, &end))
        return NULL;

    Py_ssize_t substr_len;
    const char *substr = _obj_as_string_and_size(substr_obj, &substr_len);
    if(!substr)
        return NULL;

    start = _fix_index(start, cstring_len(self));
    end = _fix_index(end, cstring_len(self));

    params->start = CSTRING_VALUE_AT(self, start);
    params->end = CSTRING_VALUE_AT(self, end);
    params->substr = substr;
    params->substr_len = substr_len;

    return params;
}

PyDoc_STRVAR(count__doc__, "");
static PyObject *cstring_count(PyObject *self, PyObject *args) {
    struct _substr_params params;

    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;

    const char *p = params.start;
    long result = 0;
    while((p = strstr(p, params.substr)) != NULL) {
        ++result;
        p += params.substr_len;
        if(p >= params.end)
            break;
    }

    return PyLong_FromLong(result);
}

static const char *_substr_params_str(const struct _substr_params *params) {
    const char *p = strstr(params->start, params->substr);
    if(!p || p + params->substr_len > params->end)
        return NULL;
    return p;
}

static const char *_substr_params_rstr(const struct _substr_params *params) {
    const char *p = params->end - params->substr_len + 1;
    for(;;) {
        p = _memrchr(params->start, *params->substr, p - params->start);
        if(!p)
            goto done;
        if(memcmp(p, params->substr, params->substr_len) == 0)
            return p;
    }
done:
    return NULL;
}

PyDoc_STRVAR(find__doc__, "");
PyObject *cstring_find(PyObject *self, PyObject *args) {
    struct _substr_params params;

    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;

    const char *p = _substr_params_str(¶ms);
    if(!p)
        return PyLong_FromLong(-1);

    return PyLong_FromSsize_t(p - CSTRING_VALUE(self));
}

PyDoc_STRVAR(index__doc__, "");
PyObject *cstring_index(PyObject *self, PyObject *args) {
    struct _substr_params params;

    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;

    const char *p = _substr_params_str(¶ms);
    if(!p) {
        PyErr_SetString(PyExc_ValueError, "substring not found");
        return NULL;
    }

    return PyLong_FromSsize_t(p - CSTRING_VALUE(self));
}

PyDoc_STRVAR(isalnum__doc__, "");
PyObject *cstring_isalnum(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(!isalnum(*p))
            Py_RETURN_FALSE;
        ++p;
    }
    Py_RETURN_TRUE;
}

PyDoc_STRVAR(isalpha__doc__, "");
PyObject *cstring_isalpha(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(!isalpha(*p))
            Py_RETURN_FALSE;
        ++p;
    }
    Py_RETURN_TRUE;
}

PyDoc_STRVAR(isdigit__doc__, "");
PyObject *cstring_isdigit(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(!isdigit(*p))
            Py_RETURN_FALSE;
        ++p;
    }
    Py_RETURN_TRUE;
}

PyDoc_STRVAR(islower__doc__, "");
PyObject *cstring_islower(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(isalpha(*p)) {
            if(!islower(*p))
                Py_RETURN_FALSE;
            ++p;
            while(*p) {
                if(isalpha(*p) && !islower(*p))
                    Py_RETURN_FALSE;
                ++p;
            }
            /* at least one lc alpha and no uc alphas */
            Py_RETURN_TRUE;
        }
        ++p;
    }
    Py_RETURN_FALSE;
}

PyDoc_STRVAR(isprintable__doc__, "");
PyObject *cstring_isprintable(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(!isprint(*p))
            Py_RETURN_FALSE;
        ++p;
    }
    Py_RETURN_TRUE;
}

PyDoc_STRVAR(isspace__doc__, "");
PyObject *cstring_isspace(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(!isspace(*p))
            Py_RETURN_FALSE;
        ++p;
    }
    return PyBool_FromLong(p != CSTRING_VALUE(self));
}

PyDoc_STRVAR(isupper__doc__, "");
PyObject *cstring_isupper(PyObject *self, PyObject *args) {
    const char *p = CSTRING_VALUE(self);
    while(*p) {
        if(isalpha(*p)) {
            if(!isupper(*p))
                Py_RETURN_FALSE;
            ++p;
            while(*p) {
                if(isalpha(*p) && !isupper(*p))
                    Py_RETURN_FALSE;
                ++p;
            }
            /* at least one uc alpha and no lc alphas */
            Py_RETURN_TRUE;
        }
        ++p;
    }
    Py_RETURN_FALSE;
}

PyDoc_STRVAR(join__doc__, "");
PyObject *cstring_join(PyObject *self, PyObject *arg) {
    PyObject *iter = PyObject_GetIter(arg);
    if(!iter)
        return NULL;

    PyObject *result = NULL;
    PyObject *item = NULL;

    while((item = PyIter_Next(iter)) != NULL) {
        if(result) {
            PyObject *next = _concat_in_place(result, self);
            if(!next)
                goto fail;
            result = next;
        }
        PyObject *next = _concat_in_place(result, item);
        if(!next)
            goto fail;
        Py_DECREF(item);
        result = next;
    }

    return result;

fail:
    Py_XDECREF(item);
    Py_XDECREF(result);
    return NULL;
}

PyDoc_STRVAR(lower__doc__, "");
PyObject *cstring_lower(PyObject *self, PyObject *args) {
    struct cstring *new = CSTRING_ALLOC(Py_TYPE(self), Py_SIZE(self));
    if(!new)
        return NULL;
    const char *s = CSTRING_VALUE(self);
    char *d = CSTRING_VALUE(new);

    while((*d++ = tolower(*s++)) != '\0')
        ;

    return (PyObject *)new;
}

static PyObject *_tuple_steal_refs(Py_ssize_t count, ...) {
    PyObject *result = PyTuple_New(count);
    if(!result)
        return NULL;

    va_list va;
    va_start(va, count);
    for(int i = 0; i < count; ++i) {
        PyObject *o = va_arg(va, PyObject *);
        if(!o)
            goto fail;
        PyTuple_SET_ITEM(result, i, o);
    }
    va_end(va);

    return result;

fail:
    Py_DECREF(result);
    return NULL;
}

PyDoc_STRVAR(partition__doc__, "");
PyObject *cstring_partition(PyObject *self, PyObject *arg) {
    if(!_ensure_cstring(arg))
        return NULL;

    const char *search = CSTRING_VALUE(arg);

    const char *left = CSTRING_VALUE(self);
    const char *mid = strstr(left, search);
    if(!mid) {
        return _tuple_steal_refs(3,
            (Py_INCREF(self), self),
            cstring_new_empty(),
            cstring_new_empty());
    }
    const char *right = mid + strlen(search);

    return _tuple_steal_refs(3,
        _cstring_new(Py_TYPE(self), left, mid - left),
        _cstring_new(Py_TYPE(self), mid, right - mid),
        _cstring_new(Py_TYPE(self), right, &CSTRING_LAST_BYTE(self) - right));
}

PyDoc_STRVAR(rpartition__doc__, "");
PyObject *cstring_rpartition(PyObject *self, PyObject *arg) {
    if(!_ensure_cstring(arg))
        return NULL;

    const char *search = CSTRING_VALUE(arg);

    const char *left = CSTRING_VALUE(self);
    const char *mid = _strrstr(left, search);
    if(!mid) {
        return _tuple_steal_refs(3,
            cstring_new_empty(),
            cstring_new_empty(),
            (Py_INCREF(self), self));
    }
    const char *right = mid + strlen(search);

    return _tuple_steal_refs(3,
        _cstring_new(Py_TYPE(self), left, mid - left),
        _cstring_new(Py_TYPE(self), mid, right - mid),
        _cstring_new(Py_TYPE(self), right, &CSTRING_LAST_BYTE(self) - right));
}

PyDoc_STRVAR(rfind__doc__, "");
PyObject *cstring_rfind(PyObject *self, PyObject *args) {
    struct _substr_params params;

    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;

    const char *p = _substr_params_rstr(¶ms);

    if(!p)
        return PyLong_FromLong(-1);

    return PyLong_FromSsize_t(p - CSTRING_VALUE(self));
}

PyDoc_STRVAR(rindex__doc__, "");
PyObject *cstring_rindex(PyObject *self, PyObject *args) {
    struct _substr_params params;

    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;

    const char *p = _substr_params_rstr(¶ms);
    if(!p) {
        PyErr_SetString(PyExc_ValueError, "substring not found");
        return NULL;
    }

    return PyLong_FromSsize_t(p - CSTRING_VALUE(self));
}

PyObject *_cstring_split_on_chars(PyObject *self, const char seps[], Py_ssize_t maxsplit) {
    if(maxsplit < 0)
        maxsplit = PY_SSIZE_T_MAX;

    const char *start = CSTRING_VALUE(self);

    PyObject *list = PyList_New(0);
    if(!list)
        return NULL;

    while(*start) {
        const char *end = start;
        while(*end && !strchr(seps, *end))
            ++end;

        PyObject *new = _cstring_new(Py_TYPE(self), start, end - start);
        if(!new)
            goto fail;
        PyList_Append(list, new);
        Py_DECREF(new);

        const char *skip = end + 1;
        while(*skip && strchr(seps, *skip))
            ++skip;
        start = skip;

        if(PyList_GET_SIZE(list) + 1 > maxsplit) {
            PyObject *new = _cstring_new(Py_TYPE(self), start, strlen(start));
            if(!new)
                goto fail;
            PyList_Append(list, new);
            Py_DECREF(new);
            break;
        }
    }

    return list;

fail:
    Py_DECREF(list);
    return NULL;
}

PyObject *_cstring_split_on_cstring(PyObject *self, PyObject *sepobj, Py_ssize_t maxsplit) {
    if(!_ensure_cstring(sepobj))
        return NULL;

    if(maxsplit < 0)
        maxsplit = PY_SSIZE_T_MAX;

    PyObject *list = PyList_New(0);
    if(!list)
        return NULL;

    const char *sep = CSTRING_VALUE(sepobj);
    const char *s = CSTRING_VALUE(self);
    for(;;) {
        const char *e = strstr(s, sep);
        if(!e)
            break;
        PyObject *new = _cstring_new(Py_TYPE(self), s, e - s);
        if(!new)
            goto fail;
        PyList_Append(list, new);
        Py_DECREF(new);
        s = e + strlen(sep);
        if(PyList_GET_SIZE(list) + 1 > maxsplit)
            break;
    }

    PyObject *new = _cstring_new(Py_TYPE(self), s, strlen(s));
    if(!new)
        goto fail;
    PyList_Append(list, new);

    return list;

fail:
    Py_DECREF(list);
    return NULL;
}

PyDoc_STRVAR(split__doc__, "");
PyObject *cstring_split(PyObject *self, PyObject *args, PyObject *kwargs) {
    PyObject *sepobj = Py_None;
    int maxsplit = -1;
    char *kwlist[] = {"sep", "maxsplit", NULL};
    if(!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi", kwlist, &sepobj, &maxsplit))
        return NULL;

    return (sepobj == Py_None)
        ? _cstring_split_on_chars(self, WHITESPACE_CHARS, maxsplit)
        : _cstring_split_on_cstring(self, sepobj, maxsplit);
}

PyDoc_STRVAR(startswith__doc__, "");
PyObject *cstring_startswith(PyObject *self, PyObject *args) {
    struct _substr_params params;
    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;
    if(params.end - params.start < params.substr_len)
        return PyBool_FromLong(0);
    int cmp = memcmp(params.start, params.substr, params.substr_len);
    return PyBool_FromLong(cmp == 0);
}

const char *_strip_chars_from_args(PyObject *args) {
    PyObject *charsobj = NULL;
    if(!PyArg_ParseTuple(args, "|O", &charsobj))
        return NULL;

    const char *chars = WHITESPACE_CHARS;

    if(charsobj && charsobj != Py_None) {
        if(!PyUnicode_Check(charsobj))
            return _bad_argument_type(charsobj);
        chars = PyUnicode_AsUTF8(charsobj);
    }

    return chars;
}

PyDoc_STRVAR(strip__doc__, "");
PyObject *cstring_strip(PyObject *self, PyObject *args) {
    const char *chars = _strip_chars_from_args(args);

    const char *start = CSTRING_VALUE(self);
    while(strchr(chars, *start))
        ++start;

    const char *end = &CSTRING_LAST_BYTE(self) - 1;
    while(strchr(chars, *end))
        --end;

    Py_ssize_t newsize = end - start + 1;
    return _cstring_new(Py_TYPE(self), start, newsize);
}

PyDoc_STRVAR(lstrip__doc__, "");
PyObject *cstring_lstrip(PyObject *self, PyObject *args) {
    const char *chars = _strip_chars_from_args(args);

    const char *start = CSTRING_VALUE(self);
    while(strchr(chars, *start))
        ++start;
    const char *end = &CSTRING_LAST_BYTE(self) - 1;

    Py_ssize_t newsize = end - start + 1;
    return _cstring_new(Py_TYPE(self), start, newsize);
}

PyDoc_STRVAR(rstrip__doc__, "");
PyObject *cstring_rstrip(PyObject *self, PyObject *args) {
    const char *chars = _strip_chars_from_args(args);

    const char *start = CSTRING_VALUE(self);
    const char *end = &CSTRING_LAST_BYTE(self) - 1;
    while(strchr(chars, *end))
        --end;

    Py_ssize_t newsize = end - start + 1;
    return _cstring_new(Py_TYPE(self), start, newsize);
}

PyDoc_STRVAR(endswith__doc__, "");
PyObject *cstring_endswith(PyObject *self, PyObject *args) {
    struct _substr_params params;
    if(!_parse_substr_args(self, args, ¶ms))
        return NULL;
    if(params.end - params.start < params.substr_len)
        return PyBool_FromLong(0);
    int cmp = memcmp(params.end - params.substr_len, params.substr, params.substr_len);
    return PyBool_FromLong(cmp == 0);
}

PyDoc_STRVAR(swapcase__doc__, "");
PyObject *cstring_swapcase(PyObject *self, PyObject *args) {
    struct cstring *new = CSTRING_ALLOC(Py_TYPE(self), Py_SIZE(self));
    if(!new)
        return NULL;
    const char *s = CSTRING_VALUE(self);
    char *d = CSTRING_VALUE(new);

    for(;*s; ++s, ++d) {
        if(islower(*s)) {
            *d = toupper(*s);
        } else if(isupper(*s)) {
            *d = tolower(*s);
        } else {
            *d = *s;
        }
    }

    return (PyObject *)new;
}

PyDoc_STRVAR(upper__doc__, "");
PyObject *cstring_upper(PyObject *self, PyObject *args) {
    struct cstring *new = CSTRING_ALLOC(Py_TYPE(self), Py_SIZE(self));
    if(!new)
        return NULL;
    const char *s = CSTRING_VALUE(self);
    char *d = CSTRING_VALUE(new);

    while((*d++ = toupper(*s++)) != '\0')
        ;

    return (PyObject *)new;
}

static PySequenceMethods cstring_as_sequence = {
    .sq_length = cstring_len,
    .sq_concat = cstring_concat,
    .sq_repeat = cstring_repeat,
    .sq_item = cstring_item,
    .sq_contains = cstring_contains,
};

static PyMappingMethods cstring_as_mapping = {
    .mp_length = cstring_len,
    .mp_subscript = cstring_subscript,
};

static PyMethodDef cstring_methods[] = {
    /* TODO: capitalize */
    /* TODO: casefold */
    /* TODO: center */
    {"count", cstring_count, METH_VARARGS, count__doc__},
    /* TODO: encode (decode???) */
    {"endswith", cstring_endswith, METH_VARARGS, endswith__doc__},
    /* TODO: expandtabs */
    {"find", cstring_find, METH_VARARGS, find__doc__},
    /* TODO: format */
    /* TODO: format_map */
    {"index", cstring_index, METH_VARARGS, index__doc__},
    {"isalnum", cstring_isalnum, METH_NOARGS, isalnum__doc__},
    {"isalpha", cstring_isalpha, METH_NOARGS, isalpha__doc__},
    /* TODO: isascii */
    /* TODO: isdecimal */
    {"isdigit", cstring_isdigit, METH_NOARGS, isdigit__doc__},
    /* TODO: isidentifier */
    {"islower", cstring_islower, METH_NOARGS, islower__doc__},
    /* TODO: isnumeric */
    {"isprintable", cstring_isprintable, METH_NOARGS, isprintable__doc__},
    {"isspace", cstring_isspace, METH_NOARGS, isspace__doc__},
    /* TODO: istitle */
    {"isupper", cstring_isupper, METH_NOARGS, isupper__doc__},
    {"join", cstring_join, METH_O, join__doc__},
    /* TODO: ljust */
    {"lower", cstring_lower, METH_NOARGS, lower__doc__},
    {"lstrip", cstring_lstrip, METH_VARARGS, lstrip__doc__},
    /* TODO: maketrans */
    {"partition", cstring_partition, METH_O, partition__doc__},
    /* TODO: removeprefix */
    /* TODO: replace */
    {"rfind", cstring_rfind, METH_VARARGS, rfind__doc__},
    {"rindex", cstring_rindex, METH_VARARGS, rindex__doc__},
    /* TODO: rjust */
    {"rpartition", cstring_rpartition, METH_O, rpartition__doc__},
    /* TODO: rsplit */
    {"rstrip", cstring_rstrip, METH_VARARGS, rstrip__doc__},
    {"split", (PyCFunction)cstring_split, METH_VARARGS | METH_KEYWORDS, split__doc__},
    /* TODO: splitlines */
    {"startswith", cstring_startswith, METH_VARARGS, startswith__doc__},
    {"strip", cstring_strip, METH_VARARGS, strip__doc__},
    {"swapcase", cstring_swapcase, METH_NOARGS, swapcase__doc__},
    /* TODO: title */
    /* TODO: translate */
    {"upper", cstring_upper, METH_NOARGS, upper__doc__},
    /* TODO: zfill */
    {0},
};

static PyTypeObject cstring_type = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "cstring.cstring",
    .tp_doc = "",
    .tp_basicsize = sizeof(struct cstring),
    .tp_itemsize = sizeof(char),
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_new = cstring_new,
    .tp_dealloc = cstring_dealloc,
    .tp_richcompare = cstring_richcompare,
    .tp_str = cstring_str,
    .tp_repr = cstring_repr,
    .tp_hash = cstring_hash,
    .tp_as_sequence = &cstring_as_sequence,
    .tp_as_mapping = &cstring_as_mapping,
    .tp_methods = cstring_methods,
};

static struct PyModuleDef module = {
    .m_base = PyModuleDef_HEAD_INIT,
    .m_name = "cstring",
    .m_doc = "",
    .m_size = 0,
    .m_methods = NULL,
};

PyMODINIT_FUNC PyInit_cstring(void) {
    if(PyType_Ready(&cstring_type) < 0)
        return NULL;
    Py_INCREF(&cstring_type);
    PyObject *m = PyModule_Create(&module);
    PyModule_AddObject(m, "cstring", (PyObject *)&cstring_type);
    return m;
}

Web Proxy Viewer  |  New URL  |  Original Page