collections.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. #include "pocketpy/collections.h"
  2. namespace pkpy
  3. {
  4. struct PyDequeIter // Iterator for the deque type
  5. {
  6. PY_CLASS(PyDequeIter, collections, _deque_iterator)
  7. PyObject *ref;
  8. bool is_reversed;
  9. std::deque<PyObject *>::iterator begin, end, current;
  10. std::deque<PyObject *>::reverse_iterator rbegin, rend, rcurrent;
  11. PyDequeIter(PyObject *ref, std::deque<PyObject *>::iterator begin, std::deque<PyObject *>::iterator end)
  12. : ref(ref), begin(begin), end(end), current(begin)
  13. {
  14. this->is_reversed = false;
  15. }
  16. PyDequeIter(PyObject *ref, std::deque<PyObject *>::reverse_iterator rbegin, std::deque<PyObject *>::reverse_iterator rend)
  17. : ref(ref), rbegin(rbegin), rend(rend), rcurrent(rbegin)
  18. {
  19. this->is_reversed = true;
  20. }
  21. void _gc_mark() const { PK_OBJ_MARK(ref); }
  22. static void _register(VM *vm, PyObject *mod, PyObject *type);
  23. };
  24. void PyDequeIter::_register(VM *vm, PyObject *mod, PyObject *type)
  25. {
  26. // Iterator for the deque type
  27. vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false;
  28. vm->bind_notimplemented_constructor<PyDequeIter>(type);
  29. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject *obj)
  30. { return obj; });
  31. vm->bind__next__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject *obj)
  32. {
  33. PyDequeIter& self = _CAST(PyDequeIter&, obj);
  34. if(self.is_reversed){
  35. if(self.rcurrent == self.rend) return vm->StopIteration;
  36. PyObject* ret = *self.rcurrent;
  37. ++self.rcurrent;
  38. return ret;
  39. }
  40. else{
  41. if(self.current == self.end) return vm->StopIteration;
  42. PyObject* ret = *self.current;
  43. ++self.current;
  44. return ret;
  45. } });
  46. }
  47. struct PyDeque
  48. {
  49. PY_CLASS(PyDeque, collections, deque);
  50. PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen); // constructor
  51. // PyDeque members
  52. std::deque<PyObject *> dequeItems;
  53. int maxlen = -1; // -1 means unbounded
  54. bool bounded = false; // if true, maxlen is not -1
  55. void insertObj(bool front, bool back, int index, PyObject *item); // insert at index, used purely for internal purposes: append, appendleft, insert methods
  56. PyObject *popObj(bool front, bool back, PyObject *item, VM *vm); // pop at index, used purely for internal purposes: pop, popleft, remove methods
  57. int findIndex(VM *vm, PyObject *obj, int start, int stop); // find the index of the given object in the deque
  58. // Special methods
  59. static void _register(VM *vm, PyObject *mod, PyObject *type); // register the type
  60. void _gc_mark() const; // needed for container types, mark all objects in the deque for gc
  61. };
  62. void PyDeque::_register(VM *vm, PyObject *mod, PyObject *type)
  63. {
  64. vm->bind(type, "__new__(cls, iterable=None, maxlen=None)",
  65. [](VM *vm, ArgsView args)
  66. {
  67. Type cls_t = PK_OBJ_GET(Type, args[0]);
  68. PyObject *iterable = args[1];
  69. PyObject *maxlen = args[2];
  70. return vm->heap.gcnew<PyDeque>(cls_t, vm, iterable, maxlen);
  71. });
  72. // gets the item at the given index, if index is negative, it will be treated as index + len(deque)
  73. // if the index is out of range, IndexError will be thrown --> required for [] operator
  74. vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  75. {
  76. PyDeque &self = _CAST(PyDeque &, _0);
  77. int index = CAST(int, _1);
  78. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  79. return self.dequeItems.at(index);
  80. });
  81. // sets the item at the given index, if index is negative, it will be treated as index + len(deque)
  82. // if the index is out of range, IndexError will be thrown --> required for [] operator
  83. vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1, PyObject* _2)
  84. {
  85. PyDeque &self = _CAST(PyDeque&, _0);
  86. int index = CAST(int, _1);
  87. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  88. self.dequeItems.at(index) = _2;
  89. });
  90. // erases the item at the given index, if index is negative, it will be treated as index + len(deque)
  91. // if the index is out of range, IndexError will be thrown --> required for [] operator
  92. vm->bind__delitem__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  93. {
  94. PyDeque &self = _CAST(PyDeque&, _0);
  95. int index = CAST(int, _1);
  96. index = vm->normalized_index(index, self.dequeItems.size()); // error is handled by the vm->normalized_index
  97. self.dequeItems.erase(self.dequeItems.begin() + index);
  98. });
  99. vm->bind__len__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  100. {
  101. PyDeque &self = _CAST(PyDeque&, _0);
  102. return (i64)self.dequeItems.size();
  103. });
  104. vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  105. {
  106. PyDeque &self = _CAST(PyDeque &, _0);
  107. return vm->heap.gcnew<PyDequeIter>(
  108. PyDequeIter::_type(vm), _0,
  109. self.dequeItems.begin(), self.dequeItems.end());
  110. });
  111. vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
  112. {
  113. if(vm->_repr_recursion_set.count(_0)) return VAR("[...]");
  114. const PyDeque &self = _CAST(PyDeque&, _0);
  115. SStream ss;
  116. ss << "deque([";
  117. vm->_repr_recursion_set.insert(_0);
  118. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  119. {
  120. ss << CAST(Str&, vm->py_repr(*it));
  121. if (it != self.dequeItems.end() - 1) ss << ", ";
  122. }
  123. vm->_repr_recursion_set.erase(_0);
  124. self.bounded ? ss << "], maxlen=" << self.maxlen << ")" : ss << "])";
  125. return VAR(ss.str());
  126. });
  127. // enables comparison between two deques, == and != are supported
  128. vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
  129. {
  130. const PyDeque &self = _CAST(PyDeque&, _0);
  131. if(!is_non_tagged_type(_0, PyDeque::_type(vm))) return vm->NotImplemented;
  132. const PyDeque &other = _CAST(PyDeque&, _1);
  133. if (self.dequeItems.size() != other.dequeItems.size()) return vm->False;
  134. for (int i = 0; i < self.dequeItems.size(); i++){
  135. if (vm->py_ne(self.dequeItems[i], other.dequeItems[i])) return vm->False;
  136. }
  137. return vm->True;
  138. });
  139. // clear the deque
  140. vm->bind(type, "clear(self) -> None",
  141. [](VM *vm, ArgsView args)
  142. {
  143. PyDeque &self = _CAST(PyDeque &, args[0]);
  144. self.dequeItems.clear();
  145. return vm->None;
  146. });
  147. // extend the deque with the given iterable
  148. vm->bind(type, "extend(self, iterable) -> None",
  149. [](VM *vm, ArgsView args)
  150. {
  151. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  152. PyDeque &self = _CAST(PyDeque &, args[0]);
  153. PyObject *it = vm->py_iter(args[1]); // strong ref
  154. PyObject *obj = vm->py_next(it);
  155. while (obj != vm->StopIteration)
  156. {
  157. self.insertObj(false, true, -1, obj);
  158. obj = vm->py_next(it);
  159. }
  160. return vm->None;
  161. });
  162. // append at the end of the deque
  163. vm->bind(type, "append(self, item) -> None",
  164. [](VM *vm, ArgsView args)
  165. {
  166. PyDeque &self = _CAST(PyDeque &, args[0]);
  167. PyObject *item = args[1];
  168. self.insertObj(false, true, -1, item);
  169. return vm->None;
  170. });
  171. // append at the beginning of the deque
  172. vm->bind(type, "appendleft(self, item) -> None",
  173. [](VM *vm, ArgsView args)
  174. {
  175. PyDeque &self = _CAST(PyDeque &, args[0]);
  176. PyObject *item = args[1];
  177. self.insertObj(true, false, -1, item);
  178. return vm->None;
  179. });
  180. // pop from the end of the deque
  181. vm->bind(type, "pop(self) -> PyObject",
  182. [](VM *vm, ArgsView args)
  183. {
  184. PyDeque &self = _CAST(PyDeque &, args[0]);
  185. if (self.dequeItems.empty())
  186. {
  187. vm->IndexError("pop from an empty deque");
  188. return vm->None;
  189. }
  190. return self.popObj(false, true, nullptr, vm);
  191. });
  192. // pop from the beginning of the deque
  193. vm->bind(type, "popleft(self) -> PyObject",
  194. [](VM *vm, ArgsView args)
  195. {
  196. PyDeque &self = _CAST(PyDeque &, args[0]);
  197. if (self.dequeItems.empty())
  198. {
  199. vm->IndexError("pop from an empty deque");
  200. return vm->None;
  201. }
  202. return self.popObj(true, false, nullptr, vm);
  203. });
  204. // shallow copy of the deque
  205. vm->bind(type, "copy(self) -> deque",
  206. [](VM *vm, ArgsView args)
  207. {
  208. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  209. PyDeque &self = _CAST(PyDeque &, args[0]);
  210. PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the empty deque
  211. PyDeque &newDeque = _CAST(PyDeque &, newDequeObj); // cast it to PyDeque so we can use its methods
  212. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  213. newDeque.insertObj(false, true, -1, *it);
  214. return newDequeObj;
  215. });
  216. // NEW: counts the number of occurences of the given object in the deque
  217. vm->bind(type, "count(self, obj) -> int",
  218. [](VM *vm, ArgsView args)
  219. {
  220. PyDeque &self = _CAST(PyDeque &, args[0]);
  221. PyObject *obj = args[1];
  222. int cnt = 0, sz = self.dequeItems.size();
  223. for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
  224. {
  225. if (vm->py_eq((*it), obj))
  226. cnt++;
  227. if (sz != self.dequeItems.size())// mutating the deque during iteration is not allowed
  228. vm->RuntimeError("deque mutated during iteration");
  229. }
  230. return VAR(cnt);
  231. });
  232. // NEW: extends the deque from the left
  233. vm->bind(type, "extendleft(self, iterable) -> None",
  234. [](VM *vm, ArgsView args)
  235. {
  236. auto _lock = vm->heap.gc_scope_lock();
  237. PyDeque &self = _CAST(PyDeque &, args[0]);
  238. PyObject *it = vm->py_iter(args[1]); // strong ref
  239. PyObject *obj = vm->py_next(it);
  240. while (obj != vm->StopIteration)
  241. {
  242. self.insertObj(true, false, -1, obj);
  243. obj = vm->py_next(it);
  244. }
  245. return vm->None;
  246. });
  247. // NEW: returns the index of the given object in the deque
  248. vm->bind(type, "index(self, obj, start=None, stop=None) -> int",
  249. [](VM *vm, ArgsView args)
  250. {
  251. // Return the position of x in the deque (at or after index start and before index stop). Returns the first match or raises ValueError if not found.
  252. PyDeque &self = _CAST(PyDeque &, args[0]);
  253. PyObject *obj = args[1];
  254. int start = 0, stop = self.dequeItems.size(); // default values
  255. if (!vm->py_eq(args[2], vm->None))
  256. start = CAST(int, args[2]);
  257. if (!vm->py_eq(args[3], vm->None))
  258. stop = CAST(int, args[3]);
  259. int index = self.findIndex(vm, obj, start, stop);
  260. if (index != -1)
  261. return VAR(index);
  262. else
  263. vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in deque");
  264. return vm->None;
  265. });
  266. // NEW: returns the index of the given object in the deque
  267. vm->bind(type, "__contains__(self, obj) -> bool",
  268. [](VM *vm, ArgsView args)
  269. {
  270. // Return the position of x in the deque (at or after index start and before index stop). Returns the first match or raises ValueError if not found.
  271. PyDeque &self = _CAST(PyDeque &, args[0]);
  272. PyObject *obj = args[1];
  273. int start = 0, stop = self.dequeItems.size(); // default values
  274. int index = self.findIndex(vm, obj, start, stop);
  275. if (index != -1)
  276. return VAR(true);
  277. return VAR(false);
  278. });
  279. // NEW: inserts the given object at the given index
  280. vm->bind(type, "insert(self, index, obj) -> None",
  281. [](VM *vm, ArgsView args)
  282. {
  283. PyDeque &self = _CAST(PyDeque &, args[0]);
  284. int index = CAST(int, args[1]);
  285. PyObject *obj = args[2];
  286. if (self.bounded && self.dequeItems.size() == self.maxlen)
  287. vm->IndexError("deque already at its maximum size");
  288. else
  289. self.insertObj(false, false, index, obj); // this index shouldn't be fixed using vm->normalized_index, pass as is
  290. return vm->None;
  291. });
  292. // NEW: removes the first occurence of the given object from the deque
  293. vm->bind(type, "remove(self, obj) -> None",
  294. [](VM *vm, ArgsView args)
  295. {
  296. PyDeque &self = _CAST(PyDeque &, args[0]);
  297. PyObject *obj = args[1];
  298. PyObject *removed = self.popObj(false, false, obj, vm);
  299. if (removed == nullptr)
  300. vm->ValueError(_CAST(Str &, vm->py_repr(obj)) + " is not in list");
  301. return vm->None;
  302. });
  303. // NEW: reverses the deque
  304. vm->bind(type, "reverse(self) -> None",
  305. [](VM *vm, ArgsView args)
  306. {
  307. PyDeque &self = _CAST(PyDeque &, args[0]);
  308. if (self.dequeItems.empty() || self.dequeItems.size() == 1)
  309. return vm->None; // handle trivial cases
  310. int sz = self.dequeItems.size();
  311. for (int i = 0; i < sz / 2; i++)
  312. {
  313. PyObject *tmp = self.dequeItems[i];
  314. self.dequeItems[i] = self.dequeItems[sz - i - 1]; // swapping
  315. self.dequeItems[sz - i - 1] = tmp;
  316. }
  317. return vm->None;
  318. });
  319. // NEW: rotates the deque by n steps
  320. vm->bind(type, "rotate(self, n=1) -> None",
  321. [](VM *vm, ArgsView args)
  322. {
  323. PyDeque &self = _CAST(PyDeque &, args[0]);
  324. int n = CAST(int, args[1]);
  325. if (n != 0 && !self.dequeItems.empty()) // trivial case
  326. {
  327. PyObject *tmp; // holds the object to be rotated
  328. int direction = n > 0 ? 1 : -1;
  329. n = abs(n);
  330. n = n % self.dequeItems.size(); // make sure n is in range
  331. while (n--)
  332. {
  333. if (direction == 1)
  334. {
  335. tmp = self.dequeItems.back();
  336. self.dequeItems.pop_back();
  337. self.dequeItems.push_front(tmp);
  338. }
  339. else
  340. {
  341. tmp = self.dequeItems.front();
  342. self.dequeItems.pop_front();
  343. self.dequeItems.push_back(tmp);
  344. }
  345. }
  346. }
  347. return vm->None;
  348. });
  349. // NEW: getter and setter of property `maxlen`
  350. vm->bind_property(
  351. type, "maxlen: int",
  352. [](VM *vm, ArgsView args)
  353. {
  354. PyDeque &self = _CAST(PyDeque &, args[0]);
  355. if (self.bounded)
  356. return VAR(self.maxlen);
  357. return vm->None;
  358. },
  359. [](VM *vm, ArgsView args)
  360. {
  361. vm->AttributeError("attribute 'maxlen' of 'collections.deque' objects is not writable");
  362. return vm->None;
  363. });
  364. // NEW: support pickle
  365. vm->bind(type, "__getnewargs__(self) -> tuple[list, int]",
  366. [](VM *vm, ArgsView args)
  367. {
  368. PyDeque &self = _CAST(PyDeque &, args[0]);
  369. Tuple ret(2);
  370. List list;
  371. for (PyObject *obj : self.dequeItems)
  372. {
  373. list.push_back(obj);
  374. }
  375. ret[0] = VAR(std::move(list));
  376. if (self.bounded)
  377. ret[1] = VAR(self.maxlen);
  378. else
  379. ret[1] = vm->None;
  380. return VAR(ret);
  381. });
  382. }
  383. /// @brief initializes a new PyDeque object, actual initialization is done in __init__
  384. PyDeque::PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen)
  385. {
  386. if (!vm->py_eq(maxlen, vm->None)) // fix the maxlen first
  387. {
  388. int tmp = CAST(int, maxlen);
  389. if (tmp < 0)
  390. vm->ValueError("maxlen must be non-negative");
  391. else
  392. {
  393. this->maxlen = tmp;
  394. this->bounded = true;
  395. }
  396. }
  397. else
  398. {
  399. this->bounded = false;
  400. this->maxlen = -1;
  401. }
  402. if (!vm->py_eq(iterable, vm->None))
  403. {
  404. this->dequeItems.clear(); // clear the deque
  405. auto _lock = vm->heap.gc_scope_lock(); // locking the heap
  406. PyObject *it = vm->py_iter(iterable); // strong ref
  407. PyObject *obj = vm->py_next(it);
  408. while (obj != vm->StopIteration)
  409. {
  410. this->insertObj(false, true, -1, obj);
  411. obj = vm->py_next(it);
  412. }
  413. }
  414. }
  415. int PyDeque::findIndex(VM *vm, PyObject *obj, int start, int stop)
  416. {
  417. // the following code is special purpose normalization for this method, taken from CPython: _collectionsmodule.c file
  418. if (start < 0)
  419. {
  420. start = this->dequeItems.size() + start; // try to fix for negative indices
  421. if (start < 0)
  422. start = 0;
  423. }
  424. if (stop < 0)
  425. {
  426. stop = this->dequeItems.size() + stop; // try to fix for negative indices
  427. if (stop < 0)
  428. stop = 0;
  429. }
  430. if (stop > this->dequeItems.size())
  431. stop = this->dequeItems.size();
  432. if (start > stop)
  433. start = stop; // end of normalization
  434. PK_ASSERT(start >= 0 && start <= this->dequeItems.size() && stop >= 0 && stop <= this->dequeItems.size() && start <= stop); // sanity check
  435. int loopSize = std::min((int)(this->dequeItems.size()), stop);
  436. int sz = this->dequeItems.size();
  437. for (int i = start; i < loopSize; i++)
  438. {
  439. if (vm->py_eq(this->dequeItems[i], obj))
  440. return i;
  441. if (sz != this->dequeItems.size())// mutating the deque during iteration is not allowed
  442. vm->RuntimeError("deque mutated during iteration");
  443. }
  444. return -1;
  445. }
  446. /// @brief pops or removes an item from the deque
  447. /// @param front if true, pop from the front of the deque
  448. /// @param back if true, pop from the back of the deque
  449. /// @param item if front and back is not set, remove the first occurence of item from the deque
  450. /// @param vm is needed for the py_eq
  451. /// @return PyObject* if front or back is set, this is a pop operation and we return a PyObject*, if front and back are not set, this is a remove operation and we return the removed item or nullptr
  452. PyObject *PyDeque::popObj(bool front, bool back, PyObject *item, VM *vm)
  453. {
  454. // error handling
  455. if (front && back)
  456. throw std::runtime_error("both front and back are set"); // this should never happen
  457. if (front || back)
  458. {
  459. // front or back is set, we don't care about item, this is a pop operation and we return a PyObject*
  460. if (this->dequeItems.empty())
  461. throw std::runtime_error("pop from an empty deque"); // shouldn't happen
  462. PyObject *obj;
  463. if (front)
  464. {
  465. obj = this->dequeItems.front();
  466. this->dequeItems.pop_front();
  467. }
  468. else
  469. {
  470. obj = this->dequeItems.back();
  471. this->dequeItems.pop_back();
  472. }
  473. return obj;
  474. }
  475. else
  476. {
  477. // front and back are not set, we care about item, this is a remove operation and we return the removed item or nullptr
  478. int sz = this->dequeItems.size();
  479. for (auto it = this->dequeItems.begin(); it != this->dequeItems.end(); ++it)
  480. {
  481. bool found = vm->py_eq((*it), item);
  482. if (sz != this->dequeItems.size()) // mutating the deque during iteration is not allowed
  483. vm->IndexError("deque mutated during iteration");
  484. if (found)
  485. {
  486. PyObject *obj = *it; // keep a reference to the object for returning
  487. this->dequeItems.erase(it);
  488. return obj;
  489. }
  490. }
  491. return nullptr; // not found
  492. }
  493. }
  494. /// @brief inserts an item into the deque
  495. /// @param front if true, insert at the front of the deque
  496. /// @param back if true, insert at the back of the deque
  497. /// @param index if front and back are not set, insert at the given index
  498. /// @param item the item to insert
  499. /// @return true if the item was inserted successfully, false if the deque is bounded and is already at its maximum size
  500. void PyDeque::insertObj(bool front, bool back, int index, PyObject *item) // assume index is not fixed using the vm->normalized_index
  501. {
  502. // error handling
  503. if (front && back)
  504. throw std::runtime_error("both front and back are set"); // this should never happen
  505. if (front || back)
  506. {
  507. // front or back is set, we don't care about index
  508. if (this->bounded)
  509. {
  510. if (this->maxlen == 0)
  511. return; // bounded and maxlen is 0, so we can't append
  512. else if (this->dequeItems.size() == this->maxlen)
  513. {
  514. if (front)
  515. this->dequeItems.pop_back(); // remove the last item
  516. else if (back)
  517. this->dequeItems.pop_front(); // remove the first item
  518. }
  519. }
  520. if (front)
  521. this->dequeItems.emplace_front(item);
  522. else if (back)
  523. this->dequeItems.emplace_back(item);
  524. }
  525. else
  526. {
  527. // front and back are not set, we care about index
  528. if (index < 0)
  529. index = this->dequeItems.size() + index; // try fixing for negative indices
  530. if (index < 0) // still negative means insert at the beginning
  531. this->dequeItems.push_front(item);
  532. else if (index >= this->dequeItems.size()) // still out of range means insert at the end
  533. this->dequeItems.push_back(item);
  534. else
  535. this->dequeItems.insert((this->dequeItems.begin() + index), item); // insert at the given index
  536. }
  537. }
  538. /// @brief marks the deque items for garbage collection
  539. void PyDeque::_gc_mark() const
  540. {
  541. for (PyObject *obj : this->dequeItems)
  542. PK_OBJ_MARK(obj);
  543. }
  544. /// @brief registers the PyDeque class
  545. /// @param vm is needed for the new_module and register_class
  546. void add_module_collections(VM *vm)
  547. {
  548. PyObject *mod = vm->new_module("collections");
  549. PyDeque::register_class(vm, mod);
  550. PyDequeIter::register_class(vm, mod);
  551. CodeObject_ code = vm->compile(kPythonLibs["collections"], "collections.py", EXEC_MODE);
  552. vm->_exec(code, mod);
  553. }
  554. } // namespace pkpypkpy