@@ -100,10 +100,72 @@ VEC FUNC(New)(Py_ssize_t size, Py_ssize_t cap) {
100100 return vec ;
101101}
102102
103- PyObject * FUNC (FromIterable )(PyObject * iterable , int64_t cap ) {
103+ #ifdef BUFFER_FORMAT_CHAR_OK
104+ inline static int buffer_format_matches (const char * fmt ) {
105+ char c = * fmt ;
106+ if (c == '@' || c == '=' ) {
107+ c = fmt [1 ];
108+ }
109+ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
110+ else if (c == '< ') { c = fmt [1 ]; }
111+ else if (c == '>' || c == '!' ) { return 0 ; }
112+ #else
113+ else if (c == '> ') { c = fmt [1 ]; }
114+ else if (c == '<' || c == '!' ) { return 0 ; }
115+ #endif
116+ return c != '\0' && BUFFER_FORMAT_CHAR_OK (c );
117+ }
118+
119+ // Try to get a compatible buffer view from 'obj'. Return 1 if successful
120+ // (view is filled and caller must call PyBuffer_Release), 0 if the object
121+ // doesn't support buffer protocol or the format doesn't match (no cleanup
122+ // needed), or -1 on error.
123+ inline static int vec_get_buffer (PyObject * obj , Py_buffer * view ) {
124+ if (PyObject_GetBuffer (obj , view , PyBUF_C_CONTIGUOUS | PyBUF_FORMAT ) != 0 ) {
125+ PyErr_Clear ();
126+ return 0 ;
127+ }
128+ if (view -> ndim == 1
129+ && view -> itemsize == sizeof (ITEM_C_TYPE )
130+ && buffer_format_matches (view -> format )) {
131+ return 1 ;
132+ }
133+ PyBuffer_Release (view );
134+ return 0 ;
135+ }
136+ #endif
137+
138+ VEC FUNC (FromIterable )(PyObject * iterable , int64_t cap ) {
139+ if (cap < 0 ) {
140+ PyErr_SetString (PyExc_ValueError , "capacity must not be negative" );
141+ return vec_error ();
142+ }
143+
144+ #ifdef BUFFER_FORMAT_CHAR_OK
145+ Py_buffer view ;
146+ int buf_ok = vec_get_buffer (iterable , & view );
147+ if (buf_ok < 0 )
148+ return vec_error ();
149+ if (buf_ok ) {
150+ Py_ssize_t n = view .len / (Py_ssize_t )sizeof (ITEM_C_TYPE );
151+ Py_ssize_t alloc_size = n > cap ? n : cap ;
152+ VEC v = vec_alloc (alloc_size );
153+ if (VEC_IS_ERROR (v )) {
154+ PyBuffer_Release (& view );
155+ return vec_error ();
156+ }
157+ if (n > 0 ) {
158+ memcpy (v .buf -> items , view .buf , n * sizeof (ITEM_C_TYPE ));
159+ }
160+ v .len = n ;
161+ PyBuffer_Release (& view );
162+ return v ;
163+ }
164+ #endif
165+
104166 VEC v = vec_alloc (cap );
105167 if (VEC_IS_ERROR (v ))
106- return NULL ;
168+ return vec_error () ;
107169 if (cap > 0 ) {
108170 memset (v .buf -> items , 0 , sizeof (ITEM_C_TYPE ) * cap );
109171 }
@@ -112,7 +174,7 @@ PyObject *FUNC(FromIterable)(PyObject *iterable, int64_t cap) {
112174 PyObject * iter = PyObject_GetIter (iterable );
113175 if (iter == NULL ) {
114176 VEC_DECREF (v );
115- return NULL ;
177+ return vec_error () ;
116178 }
117179 PyObject * item ;
118180 while ((item = PyIter_Next (iter )) != NULL ) {
@@ -121,21 +183,21 @@ PyObject *FUNC(FromIterable)(PyObject *iterable, int64_t cap) {
121183 if (IS_UNBOX_ERROR (x )) {
122184 Py_DECREF (iter );
123185 VEC_DECREF (v );
124- return NULL ;
186+ return vec_error () ;
125187 }
126188 v = FUNC (Append )(v , x );
127189 if (VEC_IS_ERROR (v )) {
128190 Py_DECREF (iter );
129191 VEC_DECREF (v );
130- return NULL ;
192+ return vec_error () ;
131193 }
132194 }
133195 Py_DECREF (iter );
134196 if (PyErr_Occurred ()) {
135197 VEC_DECREF (v );
136- return NULL ;
198+ return vec_error () ;
137199 }
138- return FUNC ( Box )( v ) ;
200+ return v ;
139201}
140202
141203static PyObject * vec_new (PyTypeObject * self , PyObject * args , PyObject * kw ) {
@@ -152,7 +214,10 @@ static PyObject *vec_new(PyTypeObject *self, PyObject *args, PyObject *kw) {
152214 if (init == NULL ) {
153215 return FUNC (Box )(FUNC (New )(0 , cap ));
154216 } else {
155- return (PyObject * )FUNC (FromIterable )(init , cap );
217+ VEC v = FUNC (FromIterable )(init , cap );
218+ if (VEC_IS_ERROR (v ))
219+ return NULL ;
220+ return FUNC (Box )(v );
156221 }
157222}
158223
@@ -332,13 +397,68 @@ VEC FUNC(Append)(VEC vec, ITEM_C_TYPE x) {
332397 }
333398}
334399
400+ // Extend 'dst' by appending 'n' items from 'items', stealing 'dst'.
401+ // Caller guarantees n > 0 and that 'items' remains valid for the call.
402+ // If force_alloc is true, always allocate a new buffer even when dst has capacity.
403+ inline static VEC vec_extend_items (
404+ VEC dst , const ITEM_C_TYPE * items , Py_ssize_t n , int force_alloc
405+ ) {
406+ if (unlikely (n > PY_SSIZE_T_MAX - dst .len )) {
407+ PyErr_NoMemory ();
408+ VEC_DECREF (dst );
409+ return vec_error ();
410+ }
411+ Py_ssize_t new_len = dst .len + n ;
412+ Py_ssize_t cap = dst .buf ? VEC_CAP (dst ) : 0 ;
413+ if (!force_alloc && new_len <= cap ) {
414+ memcpy (dst .buf -> items + dst .len , items , sizeof (ITEM_C_TYPE ) * n );
415+ dst .len = new_len ;
416+ return dst ;
417+ }
418+ Py_ssize_t new_cap = cap ;
419+ while (new_cap < new_len ) {
420+ if (unlikely (new_cap > (PY_SSIZE_T_MAX - 1 ) / 2 )) {
421+ new_cap = new_len ;
422+ break ;
423+ }
424+ new_cap = 2 * new_cap + 1 ;
425+ }
426+ VEC new = vec_alloc (new_cap );
427+ if (VEC_IS_ERROR (new )) {
428+ VEC_DECREF (dst );
429+ return vec_error ();
430+ }
431+ if (dst .len > 0 )
432+ memcpy (new .buf -> items , dst .buf -> items , sizeof (ITEM_C_TYPE ) * dst .len );
433+ memcpy (new .buf -> items + dst .len , items , sizeof (ITEM_C_TYPE ) * n );
434+ new .len = new_len ;
435+ Py_XDECREF (dst .buf );
436+ return new ;
437+ }
438+
335439// Extend 'vec' with items from 'iterable', stealing 'vec'.
336440// Return extended 'vec', or error vec on failure.
337441VEC FUNC (Extend )(VEC vec , PyObject * iterable ) {
338442 if (Py_TYPE (iterable ) == & VEC_TYPE ) {
339443 return FUNC (ExtendVec )(vec , ((VEC_OBJECT * )iterable )-> vec );
340444 }
341445
446+ #ifdef BUFFER_FORMAT_CHAR_OK
447+ Py_buffer view ;
448+ int buf_ok = vec_get_buffer (iterable , & view );
449+ if (buf_ok < 0 ) {
450+ VEC_DECREF (vec );
451+ return vec_error ();
452+ }
453+ if (buf_ok ) {
454+ Py_ssize_t n = view .len / (Py_ssize_t )sizeof (ITEM_C_TYPE );
455+ if (n > 0 )
456+ vec = vec_extend_items (vec , (const ITEM_C_TYPE * )view .buf , n , 0 );
457+ PyBuffer_Release (& view );
458+ return vec ;
459+ }
460+ #endif
461+
342462 PyObject * iter = PyObject_GetIter (iterable );
343463 if (iter == NULL ) {
344464 VEC_DECREF (vec );
@@ -372,39 +492,7 @@ VEC FUNC(Extend)(VEC vec, PyObject *iterable) {
372492VEC FUNC (ExtendVec )(VEC dst , VEC src ) {
373493 if (src .len == 0 )
374494 return dst ;
375- if (unlikely (src .len > PY_SSIZE_T_MAX - dst .len )) {
376- PyErr_NoMemory ();
377- VEC_DECREF (dst );
378- return vec_error ();
379- }
380- Py_ssize_t new_len = dst .len + src .len ;
381- Py_ssize_t cap = dst .buf ? VEC_CAP (dst ) : 0 ;
382- if (new_len <= cap && dst .buf != src .buf ) {
383- // Fast path: enough capacity and no aliasing
384- memcpy (dst .buf -> items + dst .len , src .buf -> items , sizeof (ITEM_C_TYPE ) * src .len );
385- dst .len = new_len ;
386- return dst ;
387- }
388- // Need to reallocate (or dst and src share a buffer)
389- Py_ssize_t new_cap = cap ;
390- while (new_cap < new_len ) {
391- if (unlikely (new_cap > (PY_SSIZE_T_MAX - 1 ) / 2 )) {
392- new_cap = new_len ;
393- break ;
394- }
395- new_cap = 2 * new_cap + 1 ;
396- }
397- VEC new = vec_alloc (new_cap );
398- if (VEC_IS_ERROR (new )) {
399- VEC_DECREF (dst );
400- return vec_error ();
401- }
402- if (dst .len > 0 )
403- memcpy (new .buf -> items , dst .buf -> items , sizeof (ITEM_C_TYPE ) * dst .len );
404- memcpy (new .buf -> items + dst .len , src .buf -> items , sizeof (ITEM_C_TYPE ) * src .len );
405- new .len = new_len ;
406- Py_XDECREF (dst .buf );
407- return new ;
495+ return vec_extend_items (dst , src .buf -> items , src .len , dst .buf == src .buf );
408496}
409497
410498// Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed.
@@ -575,6 +663,7 @@ NAME(API) FEATURES = {
575663 FUNC (Pop ),
576664 FUNC (Remove ),
577665 FUNC (Slice ),
666+ FUNC (FromIterable ),
578667 FUNC (Extend ),
579668 FUNC (ExtendVec ),
580669};
0 commit comments