obj_list.c
1 #include <string.h>
2 #include <kuroko/vm.h>
3 #include <kuroko/value.h>
4 #include <kuroko/memory.h>
5 #include <kuroko/util.h>
6 #include <kuroko/threads.h>
7 
8 #define LIST_WRAP_INDEX() \
9  if (index < 0) index += self->values.count; \
10  if (unlikely(index < 0 || index >= (krk_integer_type)self->values.count)) return krk_runtimeError(vm.exceptions->indexError, "list index out of range: %zd", (ssize_t)index)
11 
12 #define LIST_WRAP_SOFT(val) \
13  if (val < 0) val += self->values.count; \
14  if (val < 0) val = 0; \
15  if (val > (krk_integer_type)self->values.count) val = self->values.count
16 
17 static void _list_gcscan(KrkInstance * self) {
18  for (size_t i = 0; i < ((KrkList*)self)->values.count; ++i) {
19  krk_markValue(((KrkList*)self)->values.values[i]);
20  }
21 }
22 
23 static void _list_gcsweep(KrkInstance * self) {
24  krk_freeValueArray(&((KrkList*)self)->values);
25 }
26 
30 KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw) {
31  KrkValue outList = OBJECT_VAL(krk_newInstance(vm.baseClasses->listClass));
32  krk_push(outList);
33  krk_initValueArray(AS_LIST(outList));
34 
35  if (argc) {
36  AS_LIST(outList)->capacity = argc;
37  AS_LIST(outList)->values = KRK_GROW_ARRAY(KrkValue, AS_LIST(outList)->values, 0, argc);
38  memcpy(AS_LIST(outList)->values, argv, sizeof(KrkValue) * argc);
39  AS_LIST(outList)->count = argc;
40  }
41 
42  pthread_rwlock_init(&((KrkList*)AS_OBJECT(outList))->rwlock, NULL);
43  return krk_pop();
44 }
45 
46 #define CURRENT_CTYPE KrkList *
47 #define CURRENT_NAME self
48 
49 KRK_Method(list,__getitem__) {
50  METHOD_TAKES_EXACTLY(1);
51  if (IS_INTEGER(argv[1])) {
52  CHECK_ARG(1,int,krk_integer_type,index);
53  if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock);
54  LIST_WRAP_INDEX();
55  KrkValue result = self->values.values[index];
56  if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock);
57  return result;
58  } else if (IS_slice(argv[1])) {
59  pthread_rwlock_rdlock(&self->rwlock);
60 
61  KRK_SLICER(argv[1],self->values.count) {
62  pthread_rwlock_unlock(&self->rwlock);
63  return NONE_VAL();
64  }
65 
66  if (step == 1) {
67  krk_integer_type len = end - start;
68  KrkValue result = krk_list_of(len, &AS_LIST(argv[0])->values[start], 0);
69  pthread_rwlock_unlock(&self->rwlock);
70  return result;
71  } else {
72  /* iterate and push */
73  krk_push(NONE_VAL());
74  krk_integer_type len = 0;
75  krk_integer_type i = start;
76  while ((step < 0) ? (i > end) : (i < end)) {
77  krk_push(self->values.values[i]);
78  len++;
79  i += step;
80  }
81 
82  /* make into a list */
84  krk_currentThread.stackTop[-len-1] = result;
85  while (len) {
86  krk_pop();
87  len--;
88  }
89 
90  pthread_rwlock_unlock(&self->rwlock);
91  return krk_pop();
92  }
93  } else {
94  return TYPE_ERROR(int or slice,argv[1]);
95  }
96 }
97 
98 KRK_Method(list,__eq__) {
99  METHOD_TAKES_EXACTLY(1);
100  if (!IS_list(argv[1])) return NOTIMPL_VAL();
101  KrkList * them = AS_list(argv[1]);
102  if (self->values.count != them->values.count) return BOOLEAN_VAL(0);
103  for (size_t i = 0; i < self->values.count; ++i) {
104  if (!krk_valuesSameOrEqual(self->values.values[i], them->values.values[i])) return BOOLEAN_VAL(0);
105  }
106  return BOOLEAN_VAL(1);
107 }
108 
109 KRK_Method(list,append) {
110  METHOD_TAKES_EXACTLY(1);
111  pthread_rwlock_wrlock(&self->rwlock);
112  krk_writeValueArray(&self->values, argv[1]);
113  pthread_rwlock_unlock(&self->rwlock);
114  return NONE_VAL();
115 }
116 
117 KRK_Method(list,insert) {
118  METHOD_TAKES_EXACTLY(2);
119  CHECK_ARG(1,int,krk_integer_type,index);
120  pthread_rwlock_wrlock(&self->rwlock);
121  LIST_WRAP_SOFT(index);
122  krk_writeValueArray(&self->values, NONE_VAL());
123  memmove(
124  &self->values.values[index+1],
125  &self->values.values[index],
126  sizeof(KrkValue) * (self->values.count - index - 1)
127  );
128  self->values.values[index] = argv[2];
129  pthread_rwlock_unlock(&self->rwlock);
130  return NONE_VAL();
131 }
132 
133 KRK_Method(list,__repr__) {
134  METHOD_TAKES_NONE();
135  if (((KrkObj*)self)->flags & KRK_OBJ_FLAGS_IN_REPR) return OBJECT_VAL(S("[...]"));
136  ((KrkObj*)self)->flags |= KRK_OBJ_FLAGS_IN_REPR;
137  struct StringBuilder sb = {0};
138  pushStringBuilder(&sb, '[');
139  pthread_rwlock_rdlock(&self->rwlock);
140  for (size_t i = 0; i < self->values.count; ++i) {
141  if (!krk_pushStringBuilderFormat(&sb,"%R",self->values.values[i])) goto _error;
142  if (i + 1 < self->values.count) {
143  pushStringBuilderStr(&sb, ", ", 2);
144  }
145  }
146  pthread_rwlock_unlock(&self->rwlock);
147 
148  pushStringBuilder(&sb,']');
149  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
150  return finishStringBuilder(&sb);
151 
152 _error:
154  pthread_rwlock_unlock(&self->rwlock);
155  ((KrkObj*)self)->flags &= ~(KRK_OBJ_FLAGS_IN_REPR);
156  return NONE_VAL();
157 }
158 
159 static int _list_extend_callback(void * context, const KrkValue * values, size_t count) {
160  KrkValueArray * positionals = context;
161  if (positionals->count + count > positionals->capacity) {
162  size_t old = positionals->capacity;
163  positionals->capacity = (count == 1) ? KRK_GROW_CAPACITY(old) : (positionals->count + count);
164  positionals->values = KRK_GROW_ARRAY(KrkValue, positionals->values, old, positionals->capacity);
165  }
166 
167  for (size_t i = 0; i < count; ++i) {
168  positionals->values[positionals->count++] = values[i];
169  }
170 
171  return 0;
172 }
173 
174 KRK_Method(list,extend) {
175  METHOD_TAKES_EXACTLY(1);
176  pthread_rwlock_wrlock(&self->rwlock);
177  KrkValueArray * positionals = AS_LIST(argv[0]);
178  KrkValue other = argv[1];
179  if (krk_valuesSame(argv[0],other)) {
180  other = krk_list_of(self->values.count, self->values.values, 0);
181  }
182 
183  krk_unpackIterable(other, positionals, _list_extend_callback);
184 
185  pthread_rwlock_unlock(&self->rwlock);
186  return NONE_VAL();
187 }
188 
189 KRK_Method(list,__init__) {
190  METHOD_TAKES_AT_MOST(1);
191  krk_initValueArray(AS_LIST(argv[0]));
192  pthread_rwlock_init(&self->rwlock, NULL);
193  if (argc == 2) {
194  _list_extend(2,(KrkValue[]){argv[0],argv[1]},0);
195  }
196  return NONE_VAL();
197 }
198 
199 KRK_Method(list,__mul__) {
200  METHOD_TAKES_EXACTLY(1);
201  CHECK_ARG(1,int,krk_integer_type,howMany);
202 
203  KrkValue out = krk_list_of(0, NULL, 0);
204 
205  krk_push(out);
206 
207  for (krk_integer_type i = 0; i < howMany; i++) {
208  _list_extend(2, (KrkValue[]){out,argv[0]},0);
209  }
210 
211  return krk_pop();
212 }
213 
214 KRK_Method(list,__len__) {
215  METHOD_TAKES_NONE();
216  return INTEGER_VAL(self->values.count);
217 }
218 
219 KRK_Method(list,__contains__) {
220  METHOD_TAKES_EXACTLY(1);
221  pthread_rwlock_rdlock(&self->rwlock);
222  for (size_t i = 0; i < self->values.count; ++i) {
223  if (krk_valuesSameOrEqual(argv[1], self->values.values[i])) {
224  pthread_rwlock_unlock(&self->rwlock);
225  return BOOLEAN_VAL(1);
226  }
227  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) break;
228  }
229  pthread_rwlock_unlock(&self->rwlock);
230  return BOOLEAN_VAL(0);
231 }
232 
233 KRK_Method(list,pop) {
234  METHOD_TAKES_AT_MOST(1);
235  pthread_rwlock_wrlock(&self->rwlock);
236  krk_integer_type index = self->values.count - 1;
237  if (argc == 2) {
238  CHECK_ARG(1,int,krk_integer_type,ind);
239  index = ind;
240  }
241  LIST_WRAP_INDEX();
242  KrkValue outItem = AS_LIST(argv[0])->values[index];
243  if (index == (long)AS_LIST(argv[0])->count-1) {
244  AS_LIST(argv[0])->count--;
245  pthread_rwlock_unlock(&self->rwlock);
246  return outItem;
247  } else {
248  /* Need to move up */
249  size_t remaining = AS_LIST(argv[0])->count - index - 1;
250  memmove(&AS_LIST(argv[0])->values[index], &AS_LIST(argv[0])->values[index+1],
251  sizeof(KrkValue) * remaining);
252  AS_LIST(argv[0])->count--;
253  pthread_rwlock_unlock(&self->rwlock);
254  return outItem;
255  }
256 }
257 
258 KRK_Method(list,__setitem__) {
259  METHOD_TAKES_EXACTLY(2);
260  if (IS_INTEGER(argv[1])) {
261  CHECK_ARG(1,int,krk_integer_type,index);
262  if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_rdlock(&self->rwlock);
263  LIST_WRAP_INDEX();
264  self->values.values[index] = argv[2];
265  if (vm.globalFlags & KRK_GLOBAL_THREADS) pthread_rwlock_unlock(&self->rwlock);
266  return argv[2];
267  } else if (IS_slice(argv[1])) {
268  if (!IS_list(argv[2])) {
269  return TYPE_ERROR(list,argv[2]); /* TODO other sequence types */
270  }
271 
272  KRK_SLICER(argv[1],self->values.count) {
273  return NONE_VAL();
274  }
275 
276  if (step != 1) {
277  return krk_runtimeError(vm.exceptions->valueError, "step value unsupported");
278  }
279 
280  krk_integer_type len = end - start;
281  krk_integer_type newLen = (krk_integer_type)AS_LIST(argv[2])->count;
282 
283  for (krk_integer_type i = 0; (i < len && i < newLen); ++i) {
284  AS_LIST(argv[0])->values[start+i] = AS_LIST(argv[2])->values[i];
285  }
286 
287  while (len < newLen) {
288  FUNC_NAME(list,insert)(3, (KrkValue[]){argv[0], INTEGER_VAL(start + len), AS_LIST(argv[2])->values[len]}, 0);
289  len++;
290  }
291 
292  while (newLen < len) {
293  FUNC_NAME(list,pop)(2, (KrkValue[]){argv[0], INTEGER_VAL(start + len - 1)}, 0);
294  len--;
295  }
296 
297  return OBJECT_VAL(self);
298  } else {
299  return TYPE_ERROR(int or slice, argv[1]);
300  }
301 }
302 
303 KRK_Method(list,__delitem__) {
304  METHOD_TAKES_EXACTLY(1);
305 
306  if (IS_INTEGER(argv[1])) {
307  FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],argv[1]},0);
308  } else if (IS_slice(argv[1])) {
309  KRK_SLICER(argv[1],self->values.count) {
310  return NONE_VAL();
311  }
312 
313  if (step != 1) {
314  return krk_runtimeError(vm.exceptions->valueError, "step value unsupported");
315  }
316 
317  krk_integer_type len = end - start;
318 
319  while (len > 0) {
320  FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0],INTEGER_VAL(start)},0);
321  len--;
322  }
323  } else {
324  return TYPE_ERROR(int or slice, argv[1]);
325  }
326 
327  return NONE_VAL();
328 }
329 
330 KRK_Method(list,remove) {
331  METHOD_TAKES_EXACTLY(1);
332  pthread_rwlock_wrlock(&self->rwlock);
333  for (size_t i = 0; i < self->values.count; ++i) {
334  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) {
335  pthread_rwlock_unlock(&self->rwlock);
336  return FUNC_NAME(list,pop)(2,(KrkValue[]){argv[0], INTEGER_VAL(i)},0);
337  }
338  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
339  pthread_rwlock_unlock(&self->rwlock);
340  return NONE_VAL();
341  }
342  }
343  pthread_rwlock_unlock(&self->rwlock);
344  return krk_runtimeError(vm.exceptions->valueError, "not found");
345 }
346 
347 KRK_Method(list,clear) {
348  METHOD_TAKES_NONE();
349  pthread_rwlock_wrlock(&self->rwlock);
350  krk_freeValueArray(&self->values);
351  pthread_rwlock_unlock(&self->rwlock);
352  return NONE_VAL();
353 }
354 
355 KRK_Method(list,index) {
356  METHOD_TAKES_AT_LEAST(1);
357  METHOD_TAKES_AT_MOST(3);
358 
359  krk_integer_type min = 0;
360  krk_integer_type max = self->values.count;
361 
362  if (argc > 2) {
363  if (IS_INTEGER(argv[2]))
364  min = AS_INTEGER(argv[2]);
365  else
366  return krk_runtimeError(vm.exceptions->typeError, "%s must be int, not '%T'", "min", argv[2]);
367  }
368 
369  if (argc > 3) {
370  if (IS_INTEGER(argv[3]))
371  max = AS_INTEGER(argv[3]);
372  else
373  return krk_runtimeError(vm.exceptions->typeError, "%s must be int, not '%T'", "max", argv[3]);
374  }
375 
376  pthread_rwlock_rdlock(&self->rwlock);
377  LIST_WRAP_SOFT(min);
378  LIST_WRAP_SOFT(max);
379 
380  for (krk_integer_type i = min; i < max; ++i) {
381  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) {
382  pthread_rwlock_unlock(&self->rwlock);
383  return INTEGER_VAL(i);
384  }
385  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) {
386  pthread_rwlock_unlock(&self->rwlock);
387  return NONE_VAL();
388  }
389  }
390 
391  pthread_rwlock_unlock(&self->rwlock);
392  return krk_runtimeError(vm.exceptions->valueError, "not found");
393 }
394 
395 KRK_Method(list,count) {
396  METHOD_TAKES_EXACTLY(1);
397  krk_integer_type count = 0;
398 
399  pthread_rwlock_rdlock(&self->rwlock);
400  for (size_t i = 0; i < self->values.count; ++i) {
401  if (krk_valuesSameOrEqual(self->values.values[i], argv[1])) count++;
402  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) break;
403  }
404  pthread_rwlock_unlock(&self->rwlock);
405 
406  return INTEGER_VAL(count);
407 }
408 
409 KRK_Method(list,copy) {
410  METHOD_TAKES_NONE();
411  pthread_rwlock_rdlock(&self->rwlock);
412  KrkValue result = krk_list_of(self->values.count, self->values.values, 0);
413  pthread_rwlock_unlock(&self->rwlock);
414  return result;
415 }
416 
418 static void reverse_values(KrkValue * values, size_t n) {
419  KrkValue * end = values + n - 1;
420  while (values < end) {
421  krk_currentThread.scratchSpace[0] = *values;
422  *values = *end;
424  values++;
425  end--;
426  }
427  krk_currentThread.scratchSpace[0] = NONE_VAL();
428 }
429 
430 KRK_Method(list,reverse) {
431  METHOD_TAKES_NONE();
432  pthread_rwlock_wrlock(&self->rwlock);
433  if (self->values.count > 1) reverse_values(self->values.values, self->values.count);
434  pthread_rwlock_unlock(&self->rwlock);
435  return NONE_VAL();
436 }
437 
438 struct SortSlice {
439  KrkValue * keys;
440  KrkValue * values;
441 };
442 
444  struct SortSlice begin;
445  size_t power;
446 };
447 
448 struct Run {
449  struct SortSlice start;
450  struct SortSlice end;
451  size_t power;
452 };
453 
455 static inline void slice_advance(struct SortSlice * slice) {
456  slice->keys++;
457  if (slice->values) slice->values++;
458 }
459 
461 static inline void slice_decrement(struct SortSlice * slice) {
462  slice->keys--;
463  if (slice->values) slice->values--;
464 }
465 
466 
467 /* @brief s + 1 */
468 static struct SortSlice slice_next(struct SortSlice slice) {
469  return (struct SortSlice){slice.keys + 1, slice.values ? slice.values + 1 : NULL};
470 }
471 
473 static struct SortSlice slice_plus(struct SortSlice slice, ssize_t n) {
474  return (struct SortSlice){slice.keys + n, slice.values ? slice.values + n : NULL};
475 }
476 
478 static void copy_slice(struct SortSlice start, struct SortSlice end, struct SortSlice buffer) {
479  while (start.keys != end.keys) {
480  *buffer.keys = *start.keys;
481  if (buffer.values) *buffer.values = *start.values;
482  slice_advance(&start);
483  slice_advance(&buffer);
484  }
485 }
486 
488 static int _list_sorter(KrkValue a, KrkValue b) {
489  KrkValue comp = krk_operator_lt(a,b);
490  return (IS_NONE(comp) || (IS_BOOLEAN(comp) && AS_BOOLEAN(comp)));
491 }
492 
494 static struct SortSlice powersort_strictlyDecreasingPrefix(struct SortSlice begin, struct SortSlice end) {
495  while (begin.keys + 1 < end.keys && _list_sorter(*(begin.keys + 1), *begin.keys)) slice_advance(&begin);
496  return slice_next(begin);
497 }
498 
500 static struct SortSlice powersort_weaklyIncreasingPrefix(struct SortSlice begin, struct SortSlice end) {
501  while (begin.keys + 1 < end.keys && !_list_sorter(*(begin.keys + 1), *begin.keys)) slice_advance(&begin);
502  return slice_next(begin);
503 }
504 
516 static struct SortSlice powersort_extend_and_reverse_right(struct SortSlice begin, struct SortSlice end) {
517  struct SortSlice j = begin;
518  if (j.keys == end.keys) return j;
519  if (j.keys + 1 == end.keys) return slice_next(j);
520  if (_list_sorter(*slice_next(j).keys, *j.keys)) {
521  /* If next is strictly less than current, begin a reversed chain; we already know
522  * we can advance by one, so do that before continuing to save a comparison. */
523  j = powersort_strictlyDecreasingPrefix(slice_next(begin), end);
524  reverse_values(begin.keys, j.keys - begin.keys);
525  if (begin.values) reverse_values(begin.values, j.values - begin.values);
526  } else {
527  /* Weakly increasing means j+1 >= j; continue with that chain*/
528  j = powersort_weaklyIncreasingPrefix(slice_next(begin), end);
529  }
530  return j;
531 }
532 
539 static size_t powersort_power(size_t begin, size_t end, size_t beginA, size_t beginB, size_t endB) {
540  size_t n = end - begin;
541  unsigned long l = beginA - begin + beginB - begin;
542  unsigned long r = beginB - begin + endB - begin;
543  size_t common = 0;
544  int digitA = l >= n;
545  int digitB = r >= n;
546  while (digitA == digitB) {
547  common++;
548  if (digitA) {
549  l -= n;
550  r -= n;
551  }
552  l <<= 1;
553  r <<= 1;
554  digitA = l >= n;
555  digitB = r >= n;
556  }
557  return common + 1;
558 }
559 
572 static void powersort_merge(struct SortSlice left, struct SortSlice mid, struct SortSlice right, struct SortSlice buffer) {
573  size_t n1 = mid.keys - left.keys;
574  size_t n2 = right.keys - mid.keys;
575 
576  if (n1 <= n2) {
577  copy_slice(left, mid, buffer);
578  struct SortSlice c1 = buffer, e1 = slice_plus(buffer, n1);
579  struct SortSlice c2 = mid, e2 = right, o = left;
580 
581  while (c1.keys < e1.keys && c2.keys < e2.keys) {
582  if (!_list_sorter(*c2.keys, *c1.keys)) {
583  *o.keys = *c1.keys;
584  if (o.values) *o.values = *c1.values;
585  slice_advance(&c1);
586  } else {
587  *o.keys = *c2.keys;
588  if (o.values) *o.values = *c2.values;
589  slice_advance(&c2);
590  }
591  slice_advance(&o);
592  }
593 
594  while (c1.keys < e1.keys) {
595  *o.keys = *c1.keys;
596  if (o.values) *o.values = *c1.values;
597  slice_advance(&c1);
598  slice_advance(&o);
599  }
600  } else {
601  copy_slice(mid, right, buffer);
602 
603  struct SortSlice c1 = slice_plus(mid, -1), s1 = left, o = slice_plus(right, -1);
604  struct SortSlice c2 = slice_plus(buffer, n2 - 1), s2 = buffer;
605 
606  while (c1.keys >= s1.keys && c2.keys >= s2.keys) {
607  if (!_list_sorter(*c2.keys, *c1.keys)) {
608  *o.keys = *c2.keys;
609  if (o.values) *o.values = *c2.values;
610  slice_decrement(&c2);
611  } else {
612  *o.keys = *c1.keys;
613  if (o.values) *o.values = *c1.values;
614  slice_decrement(&c1);
615  }
616  slice_decrement(&o);
617  }
618 
619  while (c2.keys >= s2.keys) {
620  *o.keys = *c2.keys;
621  if (o.values) *o.values = *c2.values;
622  slice_decrement(&c2);
623  slice_decrement(&o);
624  }
625  }
626 }
627 
654 static void powersort(KrkList * list, KrkValue key, int reverse) {
655  size_t n = list->values.count;
656  struct SortSlice slice = {list->values.values, NULL};
657 
658  /* If there is a key function, create a separate array to store
659  * the resulting key values; shove it in a tuple so we can keep
660  * those key values from being garbage collected. */
661  if (!IS_NONE(key)) {
662  KrkTuple * _keys = krk_newTuple(n);
663  krk_push(OBJECT_VAL(_keys));
664  for (size_t i = 0; i < n; ++i) {
665  krk_push(key);
666  krk_push(list->values.values[i]);
667  _keys->values.values[i] = krk_callStack(1);
668  _keys->values.count++;
669 
670  /* If the key function threw an exception, bail early. */
671  if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) goto _end_sort;
672  }
673 
674  /* values are secondary, keys are what actually gets sorted */
675  slice.values = slice.keys;
676  slice.keys = _keys->values.values;
677  }
678 
679  /* We handle reverse sort by reversing, sorting normally, and then reversing again */
680  if (reverse) {
681  reverse_values(slice.keys, n);
682  if (slice.values) reverse_values(slice.values, n);
683  }
684 
685  /* Supposedly the absolute maximum for this is strictly less than the number of bits
686  * we can fit in a size_t, so 64 ought to cover us until someone tries porting Kuroko
687  * to one of the 128-bit architectures, but even then I don't think we can handle
688  * holding that many values in a list to begin with.
689  *
690  * stack[0] should always be empty. */
691  struct SliceAndPower stack[64] = {0};
692  int top = 0;
693 
694  /* Buffer space for the merges. We shouldn't need anywhere close to this much space,
695  * but best to be safe, and we're already allocating a bunch of space for key tuples */
696  KrkTuple * bufferSpace = krk_newTuple(slice.values ? (n * 2) : n);
697  krk_push(OBJECT_VAL(bufferSpace));
698  for (size_t i = 0; i < bufferSpace->values.capacity; ++i) bufferSpace->values.values[bufferSpace->values.count++] = NONE_VAL();
699  struct SortSlice buffer = {&bufferSpace->values.values[0], slice.values ? &bufferSpace->values.values[n] : NULL};
700 
701  /* This just take the role of the C++ iterators in the reference implementaiton */
702  struct SortSlice begin = {slice.keys, slice.values};
703  struct SortSlice end = {slice.keys + n, slice.values ? slice.values + n : NULL};
704 
705  /* Our first run starts from the left and extends as far as it can. */
706  struct Run a = {begin, powersort_extend_and_reverse_right(begin,end), 0};
707 
708  while (a.end.keys < end.keys) {
709  /* Our next run is whatever is after that, assuming the initial run isn't the whole list. */
710  struct Run b = {a.end, powersort_extend_and_reverse_right(a.end, end), 0};
711  /* I don't really understand the power part of powersort, but whatever. */
712  a.power = powersort_power(0, n, a.start.keys - begin.keys, b.start.keys - begin.keys, b.end.keys - begin.keys);
713 
714  /* While the stack has things with higher power, merge them into a */
715  while (stack[top].power > a.power) {
716  struct SliceAndPower top_run = stack[top--];
717  powersort_merge(top_run.begin, a.start, a.end, buffer);
718  a.start = top_run.begin;
719  }
720  /* Put a on top of the stack, and then replace a with b */
721  stack[++top] = (struct SliceAndPower){a.start, a.power};
722  a = (struct Run){b.start, b.end, 0};
723  }
724 
725  /* While there are things in the stack (excluding the empty 0 slot), merge them into the last a */
726  while (top > 0) {
727  struct SliceAndPower top_run = stack[top--];
728  powersort_merge(top_run.begin, a.start, end, buffer);
729  a.start = top_run.begin;
730  }
731 
732  krk_pop(); /* tuple with buffer space */
733 _end_sort:
734  if (!IS_NONE(key)) krk_pop(); /* keys tuple */
735 
736  /* If we reversed at the start, reverse again now as the list is forward-sorted */
737  if (reverse) reverse_values(list->values.values, n);
738 }
739 
740 KRK_Method(list,sort) {
741  KrkValue key = NONE_VAL();
742  int reverse = 0;
743  if (!krk_parseArgs(".|$Vp", (const char*[]){"key","reverse"}, &key, &reverse)) return NONE_VAL();
744 
745  if (self->values.count < 2) return NONE_VAL();
746 
747  pthread_rwlock_wrlock(&self->rwlock);
748  powersort(self, key, reverse);
749  pthread_rwlock_unlock(&self->rwlock);
750 
751  return NONE_VAL();
752 }
753 
754 KRK_Method(list,__add__) {
755  METHOD_TAKES_EXACTLY(1);
756  if (!IS_list(argv[1])) return TYPE_ERROR(list,argv[1]);
757 
758  pthread_rwlock_rdlock(&self->rwlock);
759  KrkValue outList = krk_list_of(self->values.count, self->values.values, 0); /* copy */
760  pthread_rwlock_unlock(&self->rwlock);
761  FUNC_NAME(list,extend)(2,(KrkValue[]){outList,argv[1]},0); /* extend */
762  return outList;
763 }
764 
765 FUNC_SIG(listiterator,__init__);
766 
767 KRK_Method(list,__iter__) {
768  METHOD_TAKES_NONE();
769  KrkInstance * output = krk_newInstance(vm.baseClasses->listiteratorClass);
770 
771  krk_push(OBJECT_VAL(output));
772  FUNC_NAME(listiterator,__init__)(2, (KrkValue[]){krk_peek(0), argv[0]},0);
773  krk_pop();
774 
775  return OBJECT_VAL(output);
776 }
777 
778 #define MAKE_LIST_COMPARE(name,op) \
779  KRK_Method(list,__ ## name ## __) { \
780  METHOD_TAKES_EXACTLY(1); \
781  if (!IS_list(argv[1])) return NOTIMPL_VAL(); \
782  KrkList * them = AS_list(argv[1]); \
783  size_t lesser = self->values.count < them->values.count ? self->values.count : them->values.count; \
784  for (size_t i = 0; i < lesser; ++i) { \
785  KrkValue a = self->values.values[i]; \
786  KrkValue b = them->values.values[i]; \
787  if (krk_valuesSameOrEqual(a,b)) continue; \
788  if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL(); \
789  return krk_operator_ ## name(a,b); \
790  } \
791  return BOOLEAN_VAL((self->values.count op them->values.count)); \
792  }
793 
794 MAKE_LIST_COMPARE(gt,>)
795 MAKE_LIST_COMPARE(lt,<)
796 MAKE_LIST_COMPARE(ge,>=)
797 MAKE_LIST_COMPARE(le,<=)
798 
799 #undef CURRENT_CTYPE
800 
801 struct ListIterator {
802  KrkInstance inst;
803  KrkValue l;
804  size_t i;
805 };
806 
807 #define CURRENT_CTYPE struct ListIterator *
808 #define IS_listiterator(o) (likely(IS_INSTANCE(o) && AS_INSTANCE(o)->_class == vm.baseClasses->listiteratorClass) || krk_isInstanceOf(o,vm.baseClasses->listiteratorClass))
809 #define AS_listiterator(o) (struct ListIterator*)AS_OBJECT(o)
810 
811 static void _listiterator_gcscan(KrkInstance * self) {
812  krk_markValue(((struct ListIterator*)self)->l);
813 }
814 
815 KRK_Method(listiterator,__init__) {
816  METHOD_TAKES_EXACTLY(1);
817  CHECK_ARG(1,list,KrkList*,list);
818  self->l = argv[1];
819  self->i = 0;
820  return NONE_VAL();
821 }
822 
823 FUNC_SIG(listiterator,__call__) {
824  static _unused const char* _method_name = "__call__";
825  if (unlikely((argc != 1))) goto _bad;
826  if (unlikely(!IS_OBJECT(argv[0]))) goto _bad;
827  if (unlikely(AS_INSTANCE(argv[0])->_class != vm.baseClasses->listiteratorClass)) goto _bad;
828 
829 _maybeGood: (void)0;
830  struct ListIterator * self = (struct ListIterator *)AS_OBJECT(argv[0]);
831 
832  KrkValue _list = self->l;
833  size_t _counter = self->i;
834  if (unlikely(_counter >= AS_LIST(_list)->count)) {
835  return argv[0];
836  } else {
837  self->i = _counter + 1;
838  return AS_LIST(_list)->values[_counter];
839  }
840 
841 _bad:
842  if (argc != 1) return NOT_ENOUGH_ARGS(name);
843  if (!krk_isInstanceOf(argv[0], vm.baseClasses->listiteratorClass)) return TYPE_ERROR(listiterator, argv[0]);
844  goto _maybeGood;
845 }
846 
847 KRK_Function(sorted) {
848  if (argc < 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","sorted","at least",1,"",argc);
849  KrkValue listOut = krk_list_of(0,NULL,0);
850  krk_push(listOut);
851  FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0);
852  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
853  FUNC_NAME(list,sort)(1,(KrkValue[]){listOut,hasKw ? argv[1] : NONE_VAL(), hasKw ? argv[2] : NONE_VAL()},hasKw);
854  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
855  return krk_pop();
856 }
857 
858 KRK_Function(reversed) {
859  /* FIXME The Python reversed() function produces an iterator and only works for things with indexing or a __reversed__ method;
860  * Building a list and reversing it like we do here is not correct! */
861  if (argc != 1) return krk_runtimeError(vm.exceptions->argumentError,"%s() takes %s %d argument%s (%d given)","reversed","exactly",1,"",argc);
862  KrkValue listOut = krk_list_of(0,NULL,0);
863  krk_push(listOut);
864  FUNC_NAME(list,extend)(2,(KrkValue[]){listOut,argv[0]},0);
865  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
866  FUNC_NAME(list,reverse)(1,&listOut,0);
867  if (!IS_NONE(krk_currentThread.currentException)) return NONE_VAL();
868  return krk_pop();
869 }
870 
871 _noexport
872 void _createAndBind_listClass(void) {
873  KrkClass * list = ADD_BASE_CLASS(vm.baseClasses->listClass, "list", vm.baseClasses->objectClass);
874  list->allocSize = sizeof(KrkList);
875  list->_ongcscan = _list_gcscan;
876  list->_ongcsweep = _list_gcsweep;
877  BIND_METHOD(list,__init__);
878  BIND_METHOD(list,__eq__);
879  BIND_METHOD(list,__getitem__);
880  BIND_METHOD(list,__setitem__);
881  BIND_METHOD(list,__delitem__);
882  BIND_METHOD(list,__len__);
883  BIND_METHOD(list,__repr__);
884  BIND_METHOD(list,__contains__);
885  BIND_METHOD(list,__iter__);
886  BIND_METHOD(list,__mul__);
887  BIND_METHOD(list,__add__);
888  BIND_METHOD(list,__lt__);
889  BIND_METHOD(list,__gt__);
890  BIND_METHOD(list,__le__);
891  BIND_METHOD(list,__ge__);
892  KRK_DOC(BIND_METHOD(list,append),
893  "@brief Add an item to the end of the list.\n"
894  "@arguments item\n\n"
895  "Adds an item to the end of a list. Appending items to a list is an amortized constant-time "
896  "operation, but may result in the reallocation of the list if not enough additional space is "
897  "available to store to the new element in the current allocation.");
898  KRK_DOC(BIND_METHOD(list,extend),
899  "@brief Add the contents of an iterable to the end of a list.\n"
900  "@argument iterable\n\n"
901  "Adds all of the elements of @p iterable to the end of the list, as if each were added individually "
902  "with @ref _list_append.");
903  KRK_DOC(BIND_METHOD(list,pop),
904  "@brief Remove and return an element from the list.\n"
905  "@arguments [index]\n\n"
906  "Removes and returns the entry at the end of the list, or at @p index if provided. "
907  "Popping from the end of the list is constant-time. Popping from the head of the list "
908  "is always O(n) as the contents of the list must be shifted.");
909  KRK_DOC(BIND_METHOD(list,insert),
910  "@brief Add an entry to the list at a given offset.\n"
911  "@arguments index, val\n\n"
912  "Adds @p val to the list at offset @p index, moving all following items back. Inserting "
913  "near the beginning of a list can be costly.");
914  KRK_DOC(BIND_METHOD(list,clear),
915  "@brief Empty a list.\n\n"
916  "Removes all entries from the list.");
917  KRK_DOC(BIND_METHOD(list,index),
918  "@brief Locate an item in the list by value.\n"
919  "@arguments val,[min,[max]]\n\n"
920  "Searches for @p val in the list and returns its index if found. If @p min is provided, "
921  "the search will begin at index @p min. If @p max is also provided, the search will end "
922  "at index @p max.\n"
923  "Raises @ref ValueError if the item is not found.");
924  KRK_DOC(BIND_METHOD(list,count),
925  "@brief Count instances of a value in the list.\n"
926  "@arguments val\n\n"
927  "Scans the list for values equal to @p val and returns the count of matching entries.");
928  KRK_DOC(BIND_METHOD(list,copy),
929  "@brief Clone a list.\n\n"
930  "Equivalent to @c list[:], creates a new list with the same items as this list.");
931  KRK_DOC(BIND_METHOD(list,remove),
932  "@brief Remove an item from the list.\n"
933  "@arguments val\n\n"
934  "Scans the list for an entry equivalent to @p val and removes it from the list.\n"
935  "Raises @ref ValueError if no matching entry is found.");
936  KRK_DOC(BIND_METHOD(list,reverse),
937  "@brief Reverse the contents of a list.\n\n"
938  "Reverses the elements of the list in-place.");
939  KRK_DOC(BIND_METHOD(list,sort),
940  "@brief Sort the contents of a list.\n\n"
941  "Performs an in-place sort of the elements in the list, returning @c None as a gentle reminder "
942  "that the sort is in-place. If a sorted copy is desired, use @ref sorted instead.");
943  krk_defineNative(&list->methods, "__class_getitem__", krk_GenericAlias)->obj.flags |= KRK_OBJ_FLAGS_FUNCTION_IS_CLASS_METHOD;
944  krk_attachNamedValue(&list->methods, "__hash__", NONE_VAL());
945  krk_finalizeClass(list);
946  KRK_DOC(list, "Mutable sequence of arbitrary values.");
947 
948  BUILTIN_FUNCTION("sorted", FUNC_NAME(krk,sorted),
949  "@brief Return a sorted representation of an iterable.\n"
950  "@arguments iterable\n\n"
951  "Creates a new, sorted list from the elements of @p iterable.");
952  BUILTIN_FUNCTION("reversed", FUNC_NAME(krk,reversed),
953  "@brief Return a reversed representation of an iterable.\n"
954  "@arguments iterable\n\n"
955  "Creates a new, reversed list from the elements of @p iterable.");
956 
957  KrkClass * listiterator = ADD_BASE_CLASS(vm.baseClasses->listiteratorClass, "listiterator", vm.baseClasses->objectClass);
958  listiterator->allocSize = sizeof(struct ListIterator);
959  listiterator->_ongcscan = _listiterator_gcscan;
960  listiterator->obj.flags |= KRK_OBJ_FLAGS_NO_INHERIT;
961  BIND_METHOD(listiterator,__init__);
962  BIND_METHOD(listiterator,__call__);
963  krk_finalizeClass(listiterator);
964 
965 }
KrkValue krk_runtimeError(KrkClass *type, const char *fmt,...)
Produce and raise an exception with a formatted message.
Definition: exceptions.c:460
Functions for dealing with garbage collection and memory allocation.
void krk_markValue(KrkValue value)
During a GC scan cycle, mark a value as used.
Definition: memory.c:334
NativeFn krk_GenericAlias
Special value for type hint expressions.
Definition: obj_typing.c:67
Type object.
Definition: object.h:215
KrkCleanupCallback _ongcsweep
C function to call when the garbage collector is discarding an instance of this class.
Definition: object.h:224
KrkCleanupCallback _ongcscan
C function to call when the garbage collector visits an instance of this class in the scan phase.
Definition: object.h:223
size_t allocSize
Size to allocate when creating instances of this class.
Definition: object.h:222
KrkObj obj
Base.
Definition: object.h:216
KrkTable methods
General attributes table.
Definition: object.h:218
void krk_finalizeClass(KrkClass *_class)
Finalize a class by collecting pointers to core methods.
Definition: vm.c:189
An object of a class.
Definition: object.h:281
KrkInstance * krk_newInstance(KrkClass *_class)
Create a new instance of the given class.
Definition: object.c:343
Mutable array of values.
Definition: object.h:335
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
Definition: obj_list.c:30
KrkValueArray values
Stores the length, capacity, and actual values of the list.
Definition: object.h:337
KrkObj obj
Base.
Definition: object.h:310
The most basic object type.
Definition: object.h:41
uint16_t flags
General object flags, mostly related to garbage collection.
Definition: object.h:43
KrkNative * krk_defineNative(KrkTable *table, const char *name, NativeFn function)
Attach a native C function to an attribute table.
Definition: vm.c:155
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
Definition: vm.c:794
KrkValue currentException
Definition: vm.h:164
KrkValue * stackTop
Definition: vm.h:159
int flags
Definition: vm.h:165
KrkValue scratchSpace[KRK_THREAD_SCRATCH_SIZE]
Definition: vm.h:169
Immutable sequence of arbitrary values.
Definition: object.h:323
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
Definition: object.h:325
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
Definition: object.c:357
Flexible vector of stack references.
Definition: value.h:75
size_t capacity
Definition: value.h:76
KrkValue * values
Definition: value.h:78
void krk_initValueArray(KrkValueArray *array)
Initialize a value array.
Definition: value.c:11
void krk_freeValueArray(KrkValueArray *array)
Release relesources used by a value array.
Definition: value.c:28
void krk_writeValueArray(KrkValueArray *array, KrkValue value)
Add a value to a value array.
Definition: value.c:17
size_t count
Definition: value.h:77
Stack reference or primative value.
int krk_isInstanceOf(KrkValue obj, const KrkClass *type)
Determine if a class is an instance or subclass of a given type.
Definition: vm.c:282
static int krk_valuesSame(KrkValue a, KrkValue b)
Compare two values by identity.
Definition: value.h:141
int krk_valuesSameOrEqual(KrkValue a, KrkValue b)
Compare two values by identity, then by equality.
Definition: value.c:96
Definition: obj_list.c:448
Inline flexible string array.
Definition: util.h:162
Convience header for providing atomic operations to threads.
Utilities for creating native bindings.
#define krk_parseArgs(f, n,...)
Parse arguments to a function while accepting keyword arguments.
Definition: util.h:360
KrkValue krk_discardStringBuilder(struct StringBuilder *sb)
Discard the contents of a string builder.
Definition: obj_str.c:1123
int krk_unpackIterable(KrkValue iterable, void *context, int callback(void *, const KrkValue *, size_t))
Unpack an iterable.
Definition: builtins.c:387
#define KRK_DOC(thing, text)
Attach documentation to a thing of various types.
Definition: util.h:304
Definitions for primitive stack references.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
KrkValue krk_callStack(int argCount)
Call a callable on the stack with argCount arguments.
Definition: vm.c:732
#define vm
Convenience macro for namespacing.
Definition: vm.h:257
KrkValue krk_operator_lt(KrkValue, KrkValue)
Compare two values, returning True if the left is less than the right.
KrkValue krk_callNativeOnStack(size_t argCount, const KrkValue *stackArgs, int hasKw, NativeFn native)
Call a native function using a reference to stack arguments safely.
Definition: vm.c:601
KrkValue krk_pop(void)
Pop the top of the stack.
Definition: vm.c:131
void krk_push(KrkValue value)
Push a stack value.
Definition: vm.c:118
KrkValue krk_peek(int distance)
Peek down from the top of the stack.
Definition: vm.c:139