Skip to content

Commit c840a3b

Browse files
authored
[mypyc] Add vec extend function to librt.vecs (#21340)
This is similar to the `append` function, but the second parameter is an iterable instead of a single item. This is fairly straightforward, but we need multiple implementations for the different vec representations. We also have separate functions for `extend` with two vec arguments, and for `extend` with any iterable as the second argument. Also support the `extend(v, v)` special case. We can later add optimized support for the buffer protocol (e.g. extend `vec[u8]` with a `bytes` object).
1 parent da21dae commit c840a3b

19 files changed

Lines changed: 917 additions & 37 deletions

mypy/typeshed/stubs/librt/librt/vecs.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ class vec(Generic[T]):
2020
def append(v: vec[T], o: T, /) -> vec[T]: ...
2121
def remove(v: vec[T], o: T, /) -> vec[T]: ...
2222
def pop(v: vec[T], i: i64 = -1, /) -> tuple[vec[T], T]: ...
23+
def extend(v: vec[T], o: Iterable[T], /) -> vec[T]: ...

mypyc/irbuild/specialize.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
join_formatted_strings,
100100
tokenizer_format_call,
101101
)
102-
from mypyc.irbuild.vec import vec_append, vec_pop, vec_remove
102+
from mypyc.irbuild.vec import vec_append, vec_extend, vec_pop, vec_remove
103103
from mypyc.primitives.bytearray_ops import isinstance_bytearray
104104
from mypyc.primitives.bytes_ops import (
105105
bytes_adjust_index_op,
@@ -1520,6 +1520,19 @@ def translate_vec_append(builder: IRBuilder, expr: CallExpr, callee: RefExpr) ->
15201520
return None
15211521

15221522

1523+
@specialize_function("librt.vecs.extend")
1524+
def translate_vec_extend(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
1525+
if len(expr.args) == 2 and expr.arg_kinds == [ARG_POS, ARG_POS]:
1526+
vec_arg = expr.args[0]
1527+
iter_arg = expr.args[1]
1528+
vec_type = builder.node_type(vec_arg)
1529+
if isinstance(vec_type, RVec):
1530+
vec_value = builder.accept(vec_arg)
1531+
iter_value = builder.accept(iter_arg)
1532+
return vec_extend(builder.builder, vec_value, iter_value, iter_arg.line)
1533+
return None
1534+
1535+
15231536
@specialize_function("librt.vecs.remove")
15241537
def translate_vec_remove(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
15251538
if len(expr.args) == 2 and expr.arg_kinds == [ARG_POS, ARG_POS]:

mypyc/irbuild/vec.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,38 @@ def vec_append(builder: LowLevelIRBuilder, vec: Value, item: Value, line: int) -
435435
return call
436436

437437

438+
def vec_extend(builder: LowLevelIRBuilder, vec: Value, iterable: Value, line: int) -> Value:
439+
vec_type = vec.type
440+
assert isinstance(vec_type, RVec)
441+
item_type = vec_type.item_type
442+
if isinstance(iterable.type, RVec) and iterable.type == vec_type:
443+
suffix = "_vec"
444+
src = iterable
445+
else:
446+
suffix = ""
447+
src = builder.coerce(iterable, object_rprimitive, line)
448+
item_type_arg: list[Value] = []
449+
api_name = vec_api_by_item_type.get(item_type)
450+
if api_name is not None:
451+
name = f"{api_name}.extend{suffix}"
452+
elif vec_type.depth() == 0:
453+
name = f"VecTApi.extend{suffix}"
454+
item_type_arg = [vec_item_type(builder, item_type, line)]
455+
else:
456+
name = f"VecNestedApi.extend{suffix}"
457+
return builder.add(
458+
CallC(
459+
name,
460+
[vec, src] + item_type_arg,
461+
vec_type,
462+
steals=[True, False] + ([False] if item_type_arg else []),
463+
is_borrowed=False,
464+
error_kind=ERR_MAGIC,
465+
line=line,
466+
)
467+
)
468+
469+
438470
def vec_pop(builder: LowLevelIRBuilder, base: Value, index: Value, line: int) -> Value:
439471
assert isinstance(base.type, RVec)
440472
vec_type = base.type

mypyc/lib-rt/vecs/librt_vecs.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,78 @@ static PyObject *vec_pop(PyObject *self, PyObject *args)
876876
return res;
877877
}
878878

879+
static PyObject *vec_extend(PyObject *self, PyObject *args)
880+
{
881+
PyObject *vec;
882+
PyObject *iterable;
883+
884+
if (!PyArg_ParseTuple(args, "OO:extend", &vec, &iterable))
885+
return NULL;
886+
887+
if (VecI64_Check(vec)) {
888+
VecI64 v = ((VecI64Object *)vec)->vec;
889+
VEC_INCREF(v);
890+
v = VecI64_Extend(v, iterable);
891+
if (VEC_IS_ERROR(v))
892+
return NULL;
893+
return VecI64_Box(v);
894+
} else if (VecU8_Check(vec)) {
895+
VecU8 v = ((VecU8Object *)vec)->vec;
896+
VEC_INCREF(v);
897+
v = VecU8_Extend(v, iterable);
898+
if (VEC_IS_ERROR(v))
899+
return NULL;
900+
return VecU8_Box(v);
901+
} else if (VecFloat_Check(vec)) {
902+
VecFloat v = ((VecFloatObject *)vec)->vec;
903+
VEC_INCREF(v);
904+
v = VecFloat_Extend(v, iterable);
905+
if (VEC_IS_ERROR(v))
906+
return NULL;
907+
return VecFloat_Box(v);
908+
} else if (VecI32_Check(vec)) {
909+
VecI32 v = ((VecI32Object *)vec)->vec;
910+
VEC_INCREF(v);
911+
v = VecI32_Extend(v, iterable);
912+
if (VEC_IS_ERROR(v))
913+
return NULL;
914+
return VecI32_Box(v);
915+
} else if (VecI16_Check(vec)) {
916+
VecI16 v = ((VecI16Object *)vec)->vec;
917+
VEC_INCREF(v);
918+
v = VecI16_Extend(v, iterable);
919+
if (VEC_IS_ERROR(v))
920+
return NULL;
921+
return VecI16_Box(v);
922+
} else if (VecBool_Check(vec)) {
923+
VecBool v = ((VecBoolObject *)vec)->vec;
924+
VEC_INCREF(v);
925+
v = VecBool_Extend(v, iterable);
926+
if (VEC_IS_ERROR(v))
927+
return NULL;
928+
return VecBool_Box(v);
929+
} else if (VecT_Check(vec)) {
930+
VecT v = ((VecTObject *)vec)->vec;
931+
size_t item_type = v.buf->item_type;
932+
VEC_INCREF(v);
933+
v = VecT_Extend(v, iterable, item_type);
934+
if (VEC_IS_ERROR(v))
935+
return NULL;
936+
return VecT_Box(v, item_type);
937+
} else if (VecNested_Check(vec)) {
938+
VecNested v = ((VecNestedObject *)vec)->vec;
939+
VEC_INCREF(v);
940+
v = VecNested_Extend(v, iterable);
941+
if (VEC_IS_ERROR(v))
942+
return NULL;
943+
return VecNested_Box(v);
944+
} else {
945+
PyErr_Format(PyExc_TypeError, "vec argument expected, got %.100s",
946+
Py_TYPE(vec)->tp_name);
947+
return NULL;
948+
}
949+
}
950+
879951
// Return the base VecType for isinstance checks
880952
static PyTypeObject *get_vec_type(void) {
881953
return &VecType;
@@ -900,6 +972,7 @@ static PyMethodDef VecsMethods[] = {
900972
{"append", vec_append, METH_VARARGS, "Append a value to the end of a vec"},
901973
{"remove", vec_remove, METH_VARARGS, "Remove first occurrence of value from a vec"},
902974
{"pop", vec_pop, METH_VARARGS, "Remove and return vec item at index (default last)"},
975+
{"extend", vec_extend, METH_VARARGS, "Extend a vec with items from an iterable"},
903976
#endif
904977
{NULL, NULL, 0, NULL} /* Sentinel */
905978
};

mypyc/lib-rt/vecs/librt_vecs.h

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,8 @@ typedef struct _VecI64API {
278278
VecI64 (*remove)(VecI64, int64_t);
279279
// TODO: Py_ssize_t
280280
VecI64 (*slice)(VecI64, int64_t, int64_t);
281-
// PyObject *(*extend)(PyObject *, PyObject *);
282-
// PyObject *(*concat)(PyObject *, PyObject *);
283-
// bool (*contains)(PyObject *, int64_t);
284-
// iter?
281+
VecI64 (*extend)(VecI64, PyObject *);
282+
VecI64 (*extend_vec)(VecI64, VecI64);
285283
} VecI64API;
286284

287285
// vec[i32] operations + type objects (stored in a capsule)
@@ -297,10 +295,8 @@ typedef struct _VecI32API {
297295
VecI32 (*remove)(VecI32, int32_t);
298296
// TODO: Py_ssize_t
299297
VecI32 (*slice)(VecI32, int64_t, int64_t);
300-
// PyObject *(*extend)(PyObject *, PyObject *);
301-
// PyObject *(*concat)(PyObject *, PyObject *);
302-
// bool (*contains)(PyObject *, int32_t);
303-
// iter?
298+
VecI32 (*extend)(VecI32, PyObject *);
299+
VecI32 (*extend_vec)(VecI32, VecI32);
304300
} VecI32API;
305301

306302
// vec[i16] operations + type objects (stored in a capsule)
@@ -316,10 +312,8 @@ typedef struct _VecI16API {
316312
VecI16 (*remove)(VecI16, int16_t);
317313
// TODO: Py_ssize_t
318314
VecI16 (*slice)(VecI16, int64_t, int64_t);
319-
// PyObject *(*extend)(PyObject *, PyObject *);
320-
// PyObject *(*concat)(PyObject *, PyObject *);
321-
// bool (*contains)(PyObject *, int16_t);
322-
// iter?
315+
VecI16 (*extend)(VecI16, PyObject *);
316+
VecI16 (*extend_vec)(VecI16, VecI16);
323317
} VecI16API;
324318

325319
// vec[u8] operations + type objects (stored in a capsule)
@@ -335,10 +329,8 @@ typedef struct _VecU8API {
335329
VecU8 (*remove)(VecU8, uint8_t);
336330
// TODO: Py_ssize_t
337331
VecU8 (*slice)(VecU8, int64_t, int64_t);
338-
// PyObject *(*extend)(PyObject *, PyObject *);
339-
// PyObject *(*concat)(PyObject *, PyObject *);
340-
// bool (*contains)(PyObject *, uint8_t);
341-
// iter?
332+
VecU8 (*extend)(VecU8, PyObject *);
333+
VecU8 (*extend_vec)(VecU8, VecU8);
342334
} VecU8API;
343335

344336
// vec[float] operations + type objects (stored in a capsule)
@@ -354,10 +346,8 @@ typedef struct _VecFloatAPI {
354346
VecFloat (*remove)(VecFloat, double);
355347
// TODO: Py_ssize_t
356348
VecFloat (*slice)(VecFloat, int64_t, int64_t);
357-
// PyObject *(*extend)(PyObject *, PyObject *);
358-
// PyObject *(*concat)(PyObject *, PyObject *);
359-
// bool (*contains)(PyObject *, double);
360-
// iter?
349+
VecFloat (*extend)(VecFloat, PyObject *);
350+
VecFloat (*extend_vec)(VecFloat, VecFloat);
361351
} VecFloatAPI;
362352

363353
// vec[bool] operations + type objects (stored in a capsule)
@@ -373,10 +363,8 @@ typedef struct _VecBoolAPI {
373363
VecBool (*remove)(VecBool, char);
374364
// TODO: Py_ssize_t
375365
VecBool (*slice)(VecBool, int64_t, int64_t);
376-
// PyObject *(*extend)(PyObject *, PyObject *);
377-
// PyObject *(*concat)(PyObject *, PyObject *);
378-
// bool (*contains)(PyObject *, char);
379-
// iter?
366+
VecBool (*extend)(VecBool, PyObject *);
367+
VecBool (*extend_vec)(VecBool, VecBool);
380368
} VecBoolAPI;
381369

382370
#ifndef MYPYC_DECLARED_tuple_T2VOO
@@ -405,10 +393,8 @@ typedef struct _VecTAPI {
405393
VecT (*remove)(VecT, PyObject *);
406394
// TODO: Py_ssize_t
407395
VecT (*slice)(VecT, int64_t, int64_t);
408-
// PyObject *(*extend)(PyObject *, PyObject *);
409-
// PyObject *(*concat)(PyObject *, PyObject *);
410-
// bool (*contains)(PyObject *, PyObject *);
411-
// iter?
396+
VecT (*extend)(VecT, PyObject *, size_t);
397+
VecT (*extend_vec)(VecT, VecT, size_t);
412398
} VecTAPI;
413399

414400

@@ -436,10 +422,8 @@ typedef struct _VecNestedAPI {
436422
VecNested (*remove)(VecNested, VecNestedBufItem);
437423
// TODO: Py_ssize_t
438424
VecNested (*slice)(VecNested, int64_t, int64_t);
439-
// PyObject *(*extend)(PyObject *, PyObject *);
440-
// PyObject *(*concat)(PyObject *, PyObject *);
441-
// bool (*contains)(PyObject *, PyObject *);
442-
// iter?
425+
VecNested (*extend)(VecNested, PyObject *);
426+
VecNested (*extend_vec)(VecNested, VecNested);
443427
} VecNestedAPI;
444428

445429
typedef struct {
@@ -539,6 +523,8 @@ static inline int VecI64_IsUnboxError(int64_t x) {
539523

540524
PyObject *VecI64_Box(VecI64);
541525
VecI64 VecI64_Append(VecI64, int64_t x);
526+
VecI64 VecI64_Extend(VecI64, PyObject *iterable);
527+
VecI64 VecI64_ExtendVec(VecI64 dst, VecI64 src);
542528
VecI64 VecI64_Remove(VecI64, int64_t x);
543529
VecI64PopResult VecI64_Pop(VecI64 v, Py_ssize_t index);
544530

@@ -569,6 +555,8 @@ static inline int VecI32_IsUnboxError(int32_t x) {
569555

570556
PyObject *VecI32_Box(VecI32);
571557
VecI32 VecI32_Append(VecI32, int32_t x);
558+
VecI32 VecI32_Extend(VecI32, PyObject *iterable);
559+
VecI32 VecI32_ExtendVec(VecI32 dst, VecI32 src);
572560
VecI32 VecI32_Remove(VecI32, int32_t x);
573561
VecI32PopResult VecI32_Pop(VecI32 v, Py_ssize_t index);
574562

@@ -599,6 +587,8 @@ static inline int VecI16_IsUnboxError(int16_t x) {
599587

600588
PyObject *VecI16_Box(VecI16);
601589
VecI16 VecI16_Append(VecI16, int16_t x);
590+
VecI16 VecI16_Extend(VecI16, PyObject *iterable);
591+
VecI16 VecI16_ExtendVec(VecI16 dst, VecI16 src);
602592
VecI16 VecI16_Remove(VecI16, int16_t x);
603593
VecI16PopResult VecI16_Pop(VecI16 v, Py_ssize_t index);
604594

@@ -632,6 +622,8 @@ static inline int VecU8_IsUnboxError(uint8_t x) {
632622

633623
PyObject *VecU8_Box(VecU8);
634624
VecU8 VecU8_Append(VecU8, uint8_t x);
625+
VecU8 VecU8_Extend(VecU8, PyObject *iterable);
626+
VecU8 VecU8_ExtendVec(VecU8 dst, VecU8 src);
635627
VecU8 VecU8_Remove(VecU8, uint8_t x);
636628
VecU8PopResult VecU8_Pop(VecU8 v, Py_ssize_t index);
637629

@@ -655,6 +647,8 @@ static inline int VecFloat_IsUnboxError(double x) {
655647

656648
PyObject *VecFloat_Box(VecFloat);
657649
VecFloat VecFloat_Append(VecFloat, double x);
650+
VecFloat VecFloat_Extend(VecFloat, PyObject *iterable);
651+
VecFloat VecFloat_ExtendVec(VecFloat dst, VecFloat src);
658652
VecFloat VecFloat_Remove(VecFloat, double x);
659653
VecFloatPopResult VecFloat_Pop(VecFloat v, Py_ssize_t index);
660654

@@ -691,6 +685,8 @@ static inline int VecBool_IsUnboxError(char x) {
691685

692686
PyObject *VecBool_Box(VecBool);
693687
VecBool VecBool_Append(VecBool, char x);
688+
VecBool VecBool_Extend(VecBool, PyObject *iterable);
689+
VecBool VecBool_ExtendVec(VecBool dst, VecBool src);
694690
VecBool VecBool_Remove(VecBool, char x);
695691
VecBoolPopResult VecBool_Pop(VecBool v, Py_ssize_t index);
696692

@@ -716,6 +712,8 @@ VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type);
716712
PyObject *VecT_FromIterable(size_t item_type, PyObject *iterable, int64_t cap);
717713
PyObject *VecT_Box(VecT vec, size_t item_type);
718714
VecT VecT_Append(VecT vec, PyObject *x, size_t item_type);
715+
VecT VecT_Extend(VecT vec, PyObject *iterable, size_t item_type);
716+
VecT VecT_ExtendVec(VecT dst, VecT src, size_t item_type);
719717
VecT VecT_Remove(VecT vec, PyObject *x);
720718
VecTPopResult VecT_Pop(VecT v, Py_ssize_t index);
721719

@@ -729,6 +727,8 @@ VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_
729727
PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *iterable, int64_t cap);
730728
PyObject *VecNested_Box(VecNested);
731729
VecNested VecNested_Append(VecNested vec, VecNestedBufItem x);
730+
VecNested VecNested_Extend(VecNested vec, PyObject *iterable);
731+
VecNested VecNested_ExtendVec(VecNested dst, VecNested src);
732732
VecNested VecNested_Remove(VecNested vec, VecNestedBufItem x);
733733
VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index);
734734

0 commit comments

Comments
 (0)