Skip to content

Commit a58c5f4

Browse files
committed
Support subscripts
1 parent c658f3c commit a58c5f4

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

src/cstring.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ struct cstring {
66
char value[];
77
};
88

9-
#define CSTRING_HASH(self) (((struct cstring *)self)->hash)
10-
#define CSTRING_VALUE(self) (((struct cstring *)self)->value)
9+
#define CSTRING_HASH(self) (((struct cstring *)self)->hash)
10+
#define CSTRING_VALUE(self) (((struct cstring *)self)->value)
11+
1112

1213
static PyObject *_cstring_new(PyTypeObject *type, const char *value, size_t len) {
1314
struct cstring *new = type->tp_alloc(type, len + 1);
@@ -17,6 +18,8 @@ static PyObject *_cstring_new(PyTypeObject *type, const char *value, size_t len)
1718
return (PyObject *)new;
1819
}
1920

21+
#define CSTRING_NEW_EMPTY(tp) (_cstring_new(tp, "", 0))
22+
2023
static PyObject *cstring_new(PyTypeObject *type, PyObject *args, PyObject **kwargs) {
2124
char *value = NULL;
2225
if(!PyArg_ParseTuple(args, "s", &value))
@@ -101,7 +104,7 @@ static PyObject *cstring_repeat(PyObject *self, Py_ssize_t count) {
101104
if(!_ensure_cstring(self))
102105
return NULL;
103106
if(count <= 0)
104-
return _cstring_new(Py_TYPE(self), "", 0);
107+
return CSTRING_NEW_EMPTY(Py_TYPE(self));
105108

106109
Py_ssize_t size = (cstring_len(self) * count) + 1;
107110

@@ -133,6 +136,42 @@ static int cstring_contains(PyObject *self, PyObject *arg) {
133136
return 0;
134137
}
135138

139+
static PyObject *_cstring_subscript_index(PyObject *self, PyObject *index) {
140+
Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError);
141+
if(PyErr_Occurred())
142+
return NULL;
143+
if(i < 0)
144+
i += cstring_len(self);
145+
return cstring_item(self, i);
146+
}
147+
148+
static PyObject *_cstring_subscript_slice(PyObject *self, PyObject *slice) {
149+
Py_ssize_t start, stop, step;
150+
if(PySlice_Unpack(slice, &start, &stop, &step) < 0)
151+
return NULL;
152+
Py_ssize_t slicelen = PySlice_AdjustIndices(cstring_len(self), &start, &stop, step);
153+
assert(slicelen >= 0);
154+
155+
struct cstring *new = Py_TYPE(self)->tp_alloc(Py_TYPE(self), slicelen + 1);
156+
char *src = &CSTRING_VALUE(self)[start];
157+
for(Py_ssize_t i = 0; i < slicelen; ++i) {
158+
new->value[i] = *src;
159+
src += step;
160+
}
161+
new->value[slicelen] = '\0';
162+
return (PyObject *)new;
163+
}
164+
165+
static PyObject *cstring_subscript(PyObject *self, PyObject *key) {
166+
if(PyIndex_Check(key))
167+
return _cstring_subscript_index(self, key);
168+
if(PySlice_Check(key))
169+
return _cstring_subscript_slice(self, key);
170+
171+
PyErr_SetString(PyExc_TypeError, "Subscript must be int or slice.");
172+
return NULL;
173+
}
174+
136175
static PySequenceMethods cstring_as_sequence = {
137176
.sq_length = cstring_len,
138177
.sq_concat = cstring_concat,
@@ -143,6 +182,7 @@ static PySequenceMethods cstring_as_sequence = {
143182

144183
static PyMappingMethods cstring_as_mapping = {
145184
.mp_length = cstring_len,
185+
.mp_subscript = cstring_subscript,
146186
};
147187

148188
static PyTypeObject cstring_type = {

test/test_mapping.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,23 @@ def test_len():
55
result = cstring('hello, world')
66
assert len(result) == 12
77

8+
9+
def test_subscript():
10+
assert cstring('hello')[1] == cstring('e')
11+
12+
13+
def test_subscript_slice_empty():
14+
assert cstring('hello')[2:1] == cstring('')
15+
16+
17+
def test_subscript_slice():
18+
assert cstring('hello')[1:4] == cstring('ell')
19+
20+
21+
def test_subscript_slice_skip():
22+
assert cstring('hello, world')[1:-1:2] == cstring('el,wr')
23+
24+
25+
def test_subscript_slice_skip_back():
26+
assert cstring('hello, world')[-1:3:-3] == cstring('do,')
27+

0 commit comments

Comments
 (0)