@@ -36,10 +36,24 @@ static PyObject *_cstring_new(PyTypeObject *type, const char *value, Py_ssize_t
3636 return (PyObject * )new ;
3737}
3838
39+ static PyObject * _cstring_realloc (PyObject * self , Py_ssize_t len ) {
40+ struct cstring * new = (struct cstring * )PyObject_Realloc (self , sizeof (struct cstring ) + len + 1 );
41+ if (!new )
42+ return PyErr_NoMemory ();
43+ Py_SET_SIZE (new , len + 1 );
44+ new -> hash = -1 ;
45+ return (PyObject * )new ;
46+ }
47+
48+ static PyObject * _cstring_copy (PyObject * self ) {
49+ return _cstring_new (Py_TYPE (self ), CSTRING_VALUE (self ), Py_SIZE (self ) - 1 );
50+ }
51+
3952static PyObject * cstring_new_empty (void ) {
4053 return _cstring_new (& cstring_type , "" , 0 );
4154}
4255
56+
4357static const char * _obj_as_string_and_size (PyObject * o , Py_ssize_t * s ) {
4458 if (PyUnicode_Check (o ))
4559 return PyUnicode_AsUTF8AndSize (o , s );
@@ -147,6 +161,29 @@ static Py_ssize_t cstring_len(PyObject *self) {
147161 return Py_SIZE (self ) - 1 ;
148162}
149163
164+ static PyObject * _concat_in_place (PyObject * self , PyObject * other ) {
165+ if (!other )
166+ return PyErr_BadArgument (), NULL ;
167+ if (!_ensure_cstring (other ))
168+ return NULL ;
169+ if (!self )
170+ return _cstring_copy (other ); /* new (mutable) copy with refcnt=1 */
171+ if (!_ensure_cstring (self ))
172+ return NULL ;
173+ if (Py_REFCNT (self ) > 1 )
174+ return PyErr_BadInternalCall (), NULL ;
175+
176+ Py_ssize_t origlen = cstring_len (self );
177+ Py_ssize_t newlen = origlen + cstring_len (other );
178+ PyObject * new = _cstring_realloc (self , newlen );
179+ if (!new )
180+ return NULL ;
181+
182+ memcpy (CSTRING_VALUE_AT (new , origlen ), CSTRING_VALUE (other ), cstring_len (other ));
183+ * CSTRING_VALUE_AT (new , newlen ) = '\0' ;
184+ return new ;
185+ }
186+
150187static PyObject * cstring_concat (PyObject * left , PyObject * right ) {
151188 if (!_ensure_cstring (left ))
152189 return NULL ;
@@ -456,6 +493,37 @@ PyObject *cstring_isupper(PyObject *self, PyObject *args) {
456493 Py_RETURN_FALSE ;
457494}
458495
496+ PyDoc_STRVAR (join__doc__ , "" );
497+ PyObject * cstring_join (PyObject * self , PyObject * arg ) {
498+ PyObject * iter = PyObject_GetIter (arg );
499+ if (!iter )
500+ return NULL ;
501+
502+ PyObject * result = NULL ;
503+ PyObject * item = NULL ;
504+
505+ while ((item = PyIter_Next (iter )) != NULL ) {
506+ if (result ) {
507+ PyObject * next = _concat_in_place (result , self );
508+ if (!next )
509+ goto fail ;
510+ result = next ;
511+ }
512+ PyObject * next = _concat_in_place (result , item );
513+ if (!next )
514+ goto fail ;
515+ Py_DECREF (item );
516+ result = next ;
517+ }
518+
519+ return result ;
520+
521+ fail :
522+ Py_XDECREF (item );
523+ Py_XDECREF (result );
524+ return NULL ;
525+ }
526+
459527PyDoc_STRVAR (lower__doc__ , "" );
460528PyObject * cstring_lower (PyObject * self , PyObject * args ) {
461529 struct cstring * new = CSTRING_ALLOC (Py_TYPE (self ), Py_SIZE (self ));
@@ -589,7 +657,7 @@ static PyMethodDef cstring_methods[] = {
589657 {"isspace" , cstring_isspace , METH_NOARGS , isspace__doc__ },
590658 /* TODO: istitle */
591659 {"isupper" , cstring_isupper , METH_NOARGS , isupper__doc__ },
592- /* TODO: join */
660+ { "join" , cstring_join , METH_O , join__doc__ },
593661 /* TODO: ljust */
594662 {"lower" , cstring_lower , METH_NOARGS , lower__doc__ },
595663 /* TODO: lstrip */
0 commit comments