fix build for --disable-gtk-doc
[swfdec.git] / swfdec / swfdec_as_array.c
blob62729e0b8a6a6b7dcda1469d0bbbf7f53cab9d94
1 /* Swfdec
2 * Copyright (C) 2007-2008 Benjamin Otte <otte@gnome.org>
3 * 2007 Pekka Lampila <pekka.lampila@iki.fi>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <stdlib.h>
26 #include <string.h>
28 #include "swfdec_as_array.h"
29 #include "swfdec_as_context.h"
30 #include "swfdec_as_frame_internal.h"
31 #include "swfdec_as_function.h"
32 #include "swfdec_as_internal.h"
33 #include "swfdec_as_native_function.h"
34 #include "swfdec_as_string.h"
35 #include "swfdec_as_strings.h"
36 #include "swfdec_movie.h"
37 #include "swfdec_utils.h"
38 #include "swfdec_debug.h"
40 /**
41 * SECTION:Arrays
42 * @title: arrays
43 * @short_description: utility functions for treating objects as arrays
45 * The array object provides some convenience functions for creating and
46 * modifying arrays.
50 * Internal helper functions
53 /* NB: type is important for overflow */
54 static gint32
55 swfdec_as_array_to_index (const char *str)
57 char *end;
58 gulong l;
60 g_return_val_if_fail (str != NULL, -1);
62 l = strtoul (str, &end, 10);
64 if (*end != 0 || l > G_MAXINT32)
65 return -1;
67 return l;
70 static gint32
71 swfdec_as_array_length_as_integer (SwfdecAsObject *object)
73 SwfdecAsValue val;
74 gint32 length;
76 g_return_val_if_fail (object != NULL, 0);
78 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_length, &val);
79 length = swfdec_as_value_to_integer (object->context, val);
81 return length;
84 /**
85 * swfdec_as_array_get_length:
86 * @array: the array
88 * Gets the current length of the @array.
90 * Returns: Current length of the @array, always >= 0
91 **/
92 gint32
93 swfdec_as_array_get_length (SwfdecAsObject *array)
95 gint32 length;
97 g_return_val_if_fail (array != NULL, 0);
99 length = swfdec_as_array_length_as_integer (array);
101 if (length < 0)
102 return 0;
104 return length;
107 static void
108 swfdec_as_array_set_length_object (SwfdecAsObject *object, gint32 length)
110 SwfdecAsValue val;
111 gboolean was_array;
113 g_return_if_fail (object != NULL);
115 was_array = object->array;
116 object->array = FALSE;
118 val = swfdec_as_value_from_integer (object->context, length);
119 swfdec_as_object_set_variable_and_flags (object, SWFDEC_AS_STR_length, &val,
120 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
122 object->array = was_array;
126 * swfdec_as_array_set_length:
127 * @array: the array
128 * @length: the new length
130 * Sets the length of the @array. Values outside the new length will be
131 * removed.
133 void
134 swfdec_as_array_set_length (SwfdecAsObject *array, gint32 length)
136 SwfdecAsValue val;
138 g_return_if_fail (array != NULL);
139 g_return_if_fail (length >= 0);
141 val = swfdec_as_value_from_integer (array->context, length);
142 swfdec_as_object_set_variable_and_flags (array,
143 SWFDEC_AS_STR_length, &val,
144 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
147 typedef struct {
148 gint32 start_index;
149 gint32 num;
150 } ForeachRemoveRangeData;
152 static gboolean
153 swfdec_as_array_foreach_remove_range (SwfdecAsObject *object,
154 const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
156 ForeachRemoveRangeData *fdata = data;
157 gint32 idx;
159 idx = swfdec_as_array_to_index (variable);
160 if (idx == -1)
161 return FALSE;
163 if (flags & SWFDEC_AS_VARIABLE_PERMANENT)
164 return FALSE;
166 if (idx >= fdata->start_index && idx < fdata->start_index + fdata->num)
167 return TRUE;
169 return FALSE;
172 void
173 swfdec_as_array_remove_range (SwfdecAsObject *object, gint32 start_index,
174 gint32 num)
176 g_return_if_fail (start_index >= 0);
177 g_return_if_fail (num >= 0);
179 if (num == 0)
180 return;
182 // to avoid foreach loop, use special case when removing just one variable
183 if (num == 1) {
184 swfdec_as_object_delete_variable (object,
185 swfdec_as_integer_to_string (object->context, start_index));
186 } else {
187 ForeachRemoveRangeData fdata = { start_index, num };
188 swfdec_as_object_foreach_remove (object,
189 swfdec_as_array_foreach_remove_range, &fdata);
193 typedef struct {
194 gint32 start_index;
195 gint32 num;
196 gint32 to_index;
197 } ForeachMoveRangeData;
199 static const char *
200 swfdec_as_array_foreach_move_range (SwfdecAsObject *object,
201 const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
203 ForeachMoveRangeData *fdata = data;
204 gint32 idx;
206 idx = swfdec_as_array_to_index (variable);
207 if (idx == -1)
208 return variable;
210 if (idx >= fdata->start_index && idx < fdata->start_index + fdata->num) {
211 return swfdec_as_integer_to_string (object->context,
212 fdata->to_index + idx - fdata->start_index);
213 } else if (idx >= fdata->to_index && idx < fdata->to_index + fdata->num) {
214 return NULL;
215 } else {
216 return variable;
220 static void
221 swfdec_as_array_move_range (SwfdecAsObject *object, gint32 from_index,
222 gint32 num, gint32 to_index)
224 ForeachMoveRangeData fdata = { from_index, num, to_index };
226 g_return_if_fail (object != NULL);
227 g_return_if_fail (from_index >= 0);
228 g_return_if_fail (num >= 0);
229 g_return_if_fail (to_index >= 0);
231 if (num == 0 || from_index == to_index)
232 return;
234 swfdec_as_object_foreach_rename (object, swfdec_as_array_foreach_move_range,
235 &fdata);
237 // only changes length if it becomes bigger, not if it becomes smaller
238 if (to_index + num > swfdec_as_array_get_length (object))
239 swfdec_as_array_set_length_object (object, to_index + num);
242 static void
243 swfdec_as_array_set_range_with_flags (SwfdecAsObject *object,
244 gint32 start_index, gint32 num, const SwfdecAsValue *value,
245 SwfdecAsVariableFlag flags)
247 gint32 i;
248 const char *var;
250 // allow negative indexes
251 g_return_if_fail (object != NULL);
252 g_return_if_fail (num >= 0);
253 g_return_if_fail (num == 0 || value != NULL);
255 for (i = 0; i < num; i++) {
256 var = swfdec_as_integer_to_string (object->context, start_index + i);
257 swfdec_as_object_set_variable_and_flags (object, var, &value[i], flags);
261 static void
262 swfdec_as_array_set_range (SwfdecAsObject *object, gint32 start_index,
263 gint32 num, const SwfdecAsValue *value)
265 swfdec_as_array_set_range_with_flags (object, start_index, num, value, 0);
268 static void
269 swfdec_as_array_append_internal (SwfdecAsObject *object, guint n,
270 const SwfdecAsValue *value)
272 // allow negative length
273 swfdec_as_array_set_range (object,
274 swfdec_as_array_length_as_integer (object), n, value);
278 * swfdec_as_array_push:
279 * @array: the array
280 * @value: the value to add
282 * Adds the given @value to the @array. This is a macro that just calls
283 * swfdec_as_array_append_with_flags().
287 * swfdec_as_array_push_with_flags:
288 * @array: the array
289 * @value: the value to add
290 * @flags: the #SwfdecAsVariableFlag flags to use
292 * Adds the given @value to the @array with the given @flags. This is a macro
293 * that just calls swfdec_as_array_append_with_flags().
297 * swfdec_as_array_append:
298 * @array: the array
299 * @n: number of values to add
300 * @values: the values to add
302 * Appends the given @values to the @array. This is a macro that just calls
303 * swfdec_as_array_append_with_flags().
307 * swfdec_as_array_append_with_flags:
308 * @array: the array
309 * @n: number of values to add
310 * @values: the values to add
311 * @flags: the #SwfdecAsVariableFlag flags to use
313 * Appends the given @values to the @array using the given @flags.
315 void
316 swfdec_as_array_append_with_flags (SwfdecAsObject *array, guint n,
317 const SwfdecAsValue *value, SwfdecAsVariableFlag flags)
319 g_return_if_fail (array != NULL);
320 g_return_if_fail (n == 0 || value != NULL);
322 // don't allow negative length
323 swfdec_as_array_set_range_with_flags (array,
324 swfdec_as_array_get_length (array), n, value, flags);
328 * swfdec_as_array_insert:
329 * @array: the array
330 * @idx: index to insert the value to
331 * @value: a #SwfdecAsValue
333 * Inserts @value to @array at given index, making room for it by moving
334 * elements to bigger indexes if necessary. This is a macro that just calls
335 * swfdec_as_array_insert_with_flags().
338 * swfdec_as_array_insert_with_flags:
339 * @array: the array
340 * @idx: index to insert the value to
341 * @value: a #SwfdecAsValue
342 * @flags: the #SwfdecAsVariableFlag flags to use
344 * Inserts @value to @array at given index using given @flags, making room for
345 * it by moving elements to bigger indexes if necessary.
347 void
348 swfdec_as_array_insert_with_flags (SwfdecAsObject *array, gint32 idx,
349 const SwfdecAsValue *value, SwfdecAsVariableFlag flags)
351 gint32 length;
353 g_return_if_fail (array != NULL);
354 g_return_if_fail (idx >= 0);
356 length = swfdec_as_array_get_length (array);
358 if (idx < length)
359 swfdec_as_array_move_range (array, idx, length - idx, idx + 1);
360 swfdec_as_array_set_range_with_flags (array, idx, 1, value, flags);
364 * swfdec_as_array_remove:
365 * @array: the array
366 * @idx: index of the value to remove
368 * Removes value at given index from the @array, elements with higher indexes
369 * will be moved towards the start of the @array.
371 void
372 swfdec_as_array_remove (SwfdecAsObject *array, gint32 idx)
374 gint32 length;
376 g_return_if_fail (array != NULL);
377 g_return_if_fail (idx >= 0);
379 length = swfdec_as_array_get_length (array);
381 if (idx >= length)
382 return;
384 swfdec_as_array_move_range (array, idx + 1, length - (idx + 1), idx);
385 swfdec_as_array_set_length (array, length - 1);
389 * swfdec_as_array_get_value:
390 * @array: the array
391 * @idx: index of the value to get
392 * @value: a pointer to #SwfdecAsValue that will be set
394 * Gets a value from given index, if the value doesn't exists an undefined
395 * value is set.
397 void
398 swfdec_as_array_get_value (SwfdecAsObject *array, gint32 idx,
399 SwfdecAsValue *value)
401 const char *var;
403 g_assert (array != NULL);
404 g_assert (idx >= 0);
405 g_assert (value != NULL);
407 var = swfdec_as_integer_to_string (array->context, idx);
408 swfdec_as_object_get_variable (array, var, value);
412 * swfdec_as_array_set_value:
413 * @array: the array
414 * @idx: index of the value to set
415 * @value: a pointer to #SwfdecAsValue
417 * Sets a @value to given index. The @array's length will be increased if
418 * necessary.
420 void
421 swfdec_as_array_set_value (SwfdecAsObject *array, gint32 idx,
422 SwfdecAsValue *value)
424 const char *var;
426 g_assert (array != NULL);
427 g_assert (idx >= 0);
429 var = swfdec_as_integer_to_string (array->context, idx);
430 swfdec_as_object_set_variable (array, var, value);
433 typedef struct {
434 SwfdecAsObject *object_to;
435 gint32 offset;
436 gint32 start_index;
437 gint32 num;
438 } ForeachAppendArrayRangeData;
440 static gboolean
441 swfdec_as_array_foreach_append_array_range (SwfdecAsObject *object,
442 const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
444 ForeachAppendArrayRangeData *fdata = data;
445 gint32 idx;
446 const char *var;
448 idx = swfdec_as_array_to_index (variable);
449 if (idx >= fdata->start_index && idx < fdata->start_index + fdata->num) {
450 var = swfdec_as_integer_to_string (fdata->object_to->context,
451 fdata->offset + (idx - fdata->start_index));
452 swfdec_as_object_set_variable (fdata->object_to, var, value);
455 return TRUE;
458 static void
459 swfdec_as_array_append_array_range (SwfdecAsObject *array_to,
460 SwfdecAsObject *object_from, gint32 start_index, gint32 num)
462 ForeachAppendArrayRangeData fdata;
464 g_return_if_fail (array_to != NULL);
465 g_return_if_fail (object_from != NULL);
466 g_return_if_fail (start_index >= 0);
468 if (num == 0)
469 return;
471 fdata.object_to = array_to;
472 fdata.offset = swfdec_as_array_get_length (array_to);
473 fdata.start_index = start_index;
474 fdata.num = num;
476 swfdec_as_array_set_length_object (fdata.object_to,
477 fdata.offset + fdata.num);
478 swfdec_as_object_foreach (object_from,
479 swfdec_as_array_foreach_append_array_range, &fdata);
482 static void
483 swfdec_as_array_append_array (SwfdecAsObject *array_to,
484 SwfdecAsObject *object_from)
486 swfdec_as_array_append_array_range (array_to, object_from, 0,
487 swfdec_as_array_get_length (object_from));
491 * The rest
495 * swfdec_as_array_new:
496 * @context: a #SwfdecAsContext
498 * Creates a new array.
500 * Returns: the new array
502 SwfdecAsObject *
503 swfdec_as_array_new (SwfdecAsContext *context)
505 SwfdecAsObject *ret;
507 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
509 ret = swfdec_as_object_new (context, NULL);
510 ret->array = TRUE;
511 swfdec_as_object_set_constructor_by_name (ret, SWFDEC_AS_STR_Array, NULL);
513 swfdec_as_array_set_length_object (ret, 0);
515 return ret;
518 /*** AS CODE ***/
520 SWFDEC_AS_NATIVE (252, 7, swfdec_as_array_join)
521 void
522 swfdec_as_array_join (SwfdecAsContext *cx, SwfdecAsObject *object, guint argc,
523 SwfdecAsValue *argv, SwfdecAsValue *ret)
525 int i;
526 const char *var, *str, *sep;
527 SwfdecAsValue val;
529 if (object == NULL || object->movie)
530 return;
532 if (argc > 0) {
533 sep = swfdec_as_value_to_string (cx, argv[0]);
534 } else {
535 sep = SWFDEC_AS_STR_COMMA;
538 // note: we don't cache length
539 if (swfdec_as_array_get_length (object) > 0) {
540 GString *string;
541 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_0, &val);
542 str = swfdec_as_value_to_string (cx, val);
543 string = g_string_new (str);
544 for (i = 1; i < swfdec_as_array_get_length (object); i++) {
545 var = swfdec_as_integer_to_string (cx, i);
546 swfdec_as_object_get_variable (object, var, &val);
547 var = swfdec_as_value_to_string (cx, val);
548 g_string_append (string, sep);
549 g_string_append (string, var);
551 str = swfdec_as_context_give_string (cx, g_string_free (string, FALSE));
552 } else {
553 str = SWFDEC_AS_STR_EMPTY;
556 SWFDEC_AS_VALUE_SET_STRING (ret, str);
559 SWFDEC_AS_NATIVE (252, 9, swfdec_as_array_toString)
560 void
561 swfdec_as_array_toString (SwfdecAsContext *cx, SwfdecAsObject *object,
562 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
564 if (object == NULL || object->movie)
565 return;
567 swfdec_as_array_join (cx, object, 0, NULL, ret);
570 SWFDEC_AS_NATIVE (252, 1, swfdec_as_array_do_push)
571 void
572 swfdec_as_array_do_push (SwfdecAsContext *cx, SwfdecAsObject *object,
573 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
575 if (object == NULL || object->movie)
576 return;
578 // if 0 args, just return the length
579 // manually set the length here to make the function work on non-Arrays
580 if (argc > 0) {
581 gint32 length = swfdec_as_array_length_as_integer (object);
582 swfdec_as_array_append_internal (object, argc, argv);
583 swfdec_as_array_set_length_object (object, length + argc);
586 *ret = swfdec_as_value_from_integer (cx, swfdec_as_array_length_as_integer (object));
589 SWFDEC_AS_NATIVE (252, 2, swfdec_as_array_do_pop)
590 void
591 swfdec_as_array_do_pop (SwfdecAsContext *cx, SwfdecAsObject *object,
592 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
594 gint32 length;
595 const char *var;
597 if (object == NULL || object->movie)
598 return;
600 // we allow negative indexes here, but not 0
601 length = swfdec_as_array_length_as_integer (object);
602 if (length == 0)
603 return;
605 var = swfdec_as_integer_to_string (object->context, length - 1);
606 swfdec_as_object_get_variable (object, var, ret);
608 swfdec_as_object_delete_variable (object, var);
610 // if Array, the length is reduced by one
611 // else the length is not reduced at all, but the variable is still deleted
612 if (object->array)
613 swfdec_as_array_set_length_object (object, length - 1);
616 SWFDEC_AS_NATIVE (252, 5, swfdec_as_array_do_unshift)
617 void
618 swfdec_as_array_do_unshift (SwfdecAsContext *cx, SwfdecAsObject *object,
619 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
621 gint32 length;
623 if (object == NULL || object->movie)
624 return;
626 if (argc) {
627 // don't allow negative length
628 length = swfdec_as_array_get_length (object);
629 swfdec_as_array_move_range (object, 0, length, argc);
630 swfdec_as_array_set_range (object, 0, argc, argv);
631 // if not Array, leave the length unchanged
632 if (!object->array)
633 swfdec_as_array_set_length_object (object, length);
636 *ret = swfdec_as_value_from_integer (cx, swfdec_as_array_get_length (object));
639 SWFDEC_AS_NATIVE (252, 4, swfdec_as_array_do_shift)
640 void
641 swfdec_as_array_do_shift (SwfdecAsContext *cx, SwfdecAsObject *object,
642 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
644 gint32 length;
645 const char *var;
647 if (object == NULL || object->movie)
648 return;
650 // don't allow negative length
651 length = swfdec_as_array_get_length (object);
652 if (length <= 0)
653 return;
655 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_0, ret);
657 swfdec_as_array_move_range (object, 1, length - 1, 0);
659 // if not Array, leave the length unchanged, and don't remove the element
660 if (object->array) {
661 swfdec_as_array_set_length_object (object, length - 1);
662 } else {
663 // we have to put the last element back, because we used move, not copy
664 SwfdecAsValue val;
665 if (length > 1) {
666 var = swfdec_as_integer_to_string (object->context, length - 2);
667 swfdec_as_object_get_variable (object, var, &val);
668 } else {
669 val = *ret;
671 var = swfdec_as_integer_to_string (object->context, length - 1);
672 swfdec_as_object_set_variable (object, var, &val);
676 static const char *
677 swfdec_as_array_foreach_reverse (SwfdecAsObject *object, const char *variable,
678 SwfdecAsValue *value, guint flags, gpointer data)
680 gint32 *length = data;
681 gint32 idx;
683 idx = swfdec_as_array_to_index (variable);
684 if (idx == -1 || idx >= *length)
685 return variable;
687 return swfdec_as_integer_to_string (object->context, *length - 1 - idx);
690 SWFDEC_AS_NATIVE (252, 11, swfdec_as_array_reverse)
691 void
692 swfdec_as_array_reverse (SwfdecAsContext *cx, SwfdecAsObject *object,
693 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
695 gint32 length;
697 if (object == NULL || object->movie)
698 return;
700 length = swfdec_as_array_get_length (object);
701 swfdec_as_object_foreach_rename (object, swfdec_as_array_foreach_reverse,
702 &length);
704 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
707 SWFDEC_AS_NATIVE (252, 3, swfdec_as_array_concat)
708 void
709 swfdec_as_array_concat (SwfdecAsContext *cx, SwfdecAsObject *object,
710 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
712 guint j;
713 SwfdecAsObject *array_new;
714 const char *var;
716 if (object == NULL || object->movie)
717 return;
719 array_new = swfdec_as_array_new (cx);
721 swfdec_as_array_append_array (array_new, object);
723 for (j = 0; j < argc; j++) {
724 if (SWFDEC_AS_VALUE_IS_OBJECT (argv[j]) &&
725 SWFDEC_AS_VALUE_GET_OBJECT (argv[j])->array)
727 swfdec_as_array_append_array (array_new,
728 SWFDEC_AS_VALUE_GET_OBJECT (argv[j]));
730 else
732 var = swfdec_as_integer_to_string (cx, swfdec_as_array_get_length (array_new));
733 swfdec_as_object_set_variable (array_new, var, &argv[j]);
737 SWFDEC_AS_VALUE_SET_OBJECT (ret, array_new);
740 SWFDEC_AS_NATIVE (252, 6, swfdec_as_array_slice)
741 void
742 swfdec_as_array_slice (SwfdecAsContext *cx, SwfdecAsObject *object, guint argc,
743 SwfdecAsValue *argv, SwfdecAsValue *ret)
745 gint32 length, start_index, num;
746 SwfdecAsObject *array_new;
748 if (object == NULL || object->movie)
749 return;
751 length = swfdec_as_array_get_length (object);
753 if (argc > 0) {
754 start_index = swfdec_as_value_to_integer (cx, argv[0]);
755 if (start_index < 0)
756 start_index = length + start_index;
757 start_index = CLAMP (start_index, 0, length);
758 } else {
759 start_index = 0;
762 if (argc > 1) {
763 gint32 endIndex = swfdec_as_value_to_integer (cx, argv[1]);
764 if (endIndex < 0)
765 endIndex = length + endIndex;
766 endIndex = CLAMP (endIndex, start_index, length);
767 num = endIndex - start_index;
768 } else {
769 num = length - start_index;
772 array_new = swfdec_as_array_new (cx);
774 swfdec_as_array_append_array_range (array_new, object, start_index, num);
776 SWFDEC_AS_VALUE_SET_OBJECT (ret, array_new);
779 SWFDEC_AS_NATIVE (252, 8, swfdec_as_array_splice)
780 void
781 swfdec_as_array_splice (SwfdecAsContext *cx, SwfdecAsObject *object,
782 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
784 gint32 length, start_index, num_remove, num_add, at_end;
785 SwfdecAsObject *array_new;
787 if (object == NULL || object->movie || argc == 0)
788 return;
790 length = swfdec_as_array_get_length (object);
792 start_index = swfdec_as_value_to_integer (cx, argv[0]);
793 if (start_index < 0)
794 start_index = length + start_index;
795 start_index = CLAMP (start_index, 0, length);
797 if (argc > 1) {
798 int tmp = swfdec_as_value_to_integer (cx, argv[1]);
799 if (tmp < 0)
800 return;
801 num_remove = MIN (tmp, length - start_index);
802 } else {
803 num_remove = length - start_index;
806 num_add = (argc > 2 ? argc - 2 : 0);
807 at_end = length - num_remove - start_index;
809 /* create return value */
810 array_new = swfdec_as_array_new (cx);
811 swfdec_as_array_append_array_range (array_new, object, start_index,
812 num_remove);
813 SWFDEC_AS_VALUE_SET_OBJECT (ret, array_new);
815 /* move old data to the right spot */
816 swfdec_as_array_move_range (object, start_index + num_remove,
817 at_end, start_index + num_add);
818 if (num_remove > at_end) {
819 swfdec_as_array_remove_range (object, start_index + at_end + num_add,
820 length - (start_index + at_end + num_add));
822 if (num_remove > num_add)
823 swfdec_as_array_set_length_object (object, length - (num_remove - num_add));
825 /* add new data */
826 if (argc > 2)
827 swfdec_as_array_set_range (object, start_index, argc - 2, argv + 2);
830 // Sorting
832 typedef enum {
833 SORT_OPTION_CASEINSENSITIVE = 1 << 0,
834 SORT_OPTION_DESCENDING = 1 << 1,
835 SORT_OPTION_UNIQUESORT = 1 << 2,
836 SORT_OPTION_RETURNINDEXEDARRAY = 1 << 3,
837 SORT_OPTION_NUMERIC = 1 << 4
838 } SortOption;
839 #define MASK_SORT_OPTION ((1 << 5) - 1)
841 typedef struct {
842 gint32 index_;
843 SwfdecAsValue value;
844 } SortEntry;
846 // inner function for swfdec_as_array_sort_compare
847 static int
848 swfdec_as_array_sort_compare_values (SwfdecAsContext *cx,
849 const SwfdecAsValue *a, const SwfdecAsValue *b, SortOption options,
850 SwfdecAsFunction *custom_function)
852 int retval;
854 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx), 0);
855 g_return_val_if_fail (custom_function == NULL ||
856 SWFDEC_IS_AS_FUNCTION (custom_function), 0);
858 if (custom_function != NULL)
860 SwfdecAsValue argv[2] = { *a, *b };
861 SwfdecAsValue ret;
862 swfdec_as_function_call (custom_function, NULL, 2, argv, &ret);
863 retval = swfdec_as_value_to_integer (cx, ret);
865 else if (options & SORT_OPTION_NUMERIC &&
866 (SWFDEC_AS_VALUE_IS_NUMBER (*a) ||
867 SWFDEC_AS_VALUE_IS_NUMBER (*b)) &&
868 !SWFDEC_AS_VALUE_IS_UNDEFINED (*a) &&
869 !SWFDEC_AS_VALUE_IS_UNDEFINED (*b))
871 if (!SWFDEC_AS_VALUE_IS_NUMBER (*a)) {
872 retval = 1;
873 } else if (!SWFDEC_AS_VALUE_IS_NUMBER (*b)) {
874 retval = -1;
875 } else {
876 double an = swfdec_as_value_to_number (cx, *a);
877 double bn = swfdec_as_value_to_number (cx, *b);
878 retval = (an < bn ? -1 : (an > bn ? 1 : 0));
881 else
883 // can't pass swfdec_as_value_to_string calls directly to compare
884 // functions, since the order of these is important
885 const char *a_str = swfdec_as_value_to_string (cx, *a);
886 const char *b_str = swfdec_as_value_to_string (cx, *b);
888 if (options & SORT_OPTION_CASEINSENSITIVE) {
889 retval = g_ascii_strcasecmp (a_str, b_str);
890 } else {
891 retval = strcmp (a_str, b_str);
895 if (options & SORT_OPTION_DESCENDING)
896 retval = -retval;
898 return retval;
901 typedef struct {
902 SwfdecAsContext * context;
903 const char ** fields;
904 const SortOption * options;
905 SwfdecAsFunction * custom_function;
906 gboolean equal_found;
907 } SortCompareData;
909 static int
910 swfdec_as_array_sort_compare (gconstpointer a_ptr, gconstpointer b_ptr,
911 gpointer user_data)
913 const SwfdecAsValue *a = &((SortEntry *)a_ptr)->value;
914 const SwfdecAsValue *b = &((SortEntry *)b_ptr)->value;
915 SortCompareData *data = user_data;
916 int retval;
918 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (data->context), 0);
919 g_return_val_if_fail (data->options != NULL, 0);
920 g_return_val_if_fail (data->custom_function == NULL ||
921 SWFDEC_IS_AS_FUNCTION (data->custom_function), 0);
922 g_return_val_if_fail (data->fields == NULL || data->fields[0] != NULL, 0);
924 if (data->fields == NULL) {
925 retval = swfdec_as_array_sort_compare_values (data->context, a, b,
926 data->options[0], data->custom_function);
927 } else {
928 SwfdecAsValue a_comp, b_comp;
929 SwfdecAsObject *object;
930 int i;
932 i = 0;
933 do {
934 object = swfdec_as_value_to_object (data->context, *a);
935 if (object) {
936 swfdec_as_object_get_variable (object, data->fields[i], &a_comp);
937 } else {
938 SWFDEC_AS_VALUE_SET_UNDEFINED (&a_comp);
941 object = swfdec_as_value_to_object (data->context, *b);
942 if (object) {
943 swfdec_as_object_get_variable (object, data->fields[i], &b_comp);
944 } else {
945 SWFDEC_AS_VALUE_SET_UNDEFINED (&b_comp);
948 retval =
949 swfdec_as_array_sort_compare_values (data->context, &a_comp, &b_comp,
950 data->options[i], data->custom_function);
951 } while (retval == 0 && data->fields[++i] != NULL);
954 if (retval == 0)
955 data->equal_found = TRUE;
957 return retval;
960 static void
961 swfdec_as_array_swap (SortEntry *array, gint32 a, gint32 b)
963 SortEntry tmp;
965 g_return_if_fail (array != NULL);
966 g_return_if_fail (a >= 0);
967 g_return_if_fail (b >= 0);
969 tmp = array[a];
970 array[a] = array[b];
971 array[b] = tmp;
974 // what an awesome function name...
975 static void
976 swfdec_as_array_sort_array (SortEntry *array, gint32 length,
977 GCompareDataFunc compare, gpointer user_data)
979 gint32 begin, end;
981 g_return_if_fail (array != NULL);
982 g_return_if_fail (length >= 0);
983 g_return_if_fail (compare != NULL);
985 if (length <= 1)
986 return;
988 // swap the first bigger element with the last smaller element
989 begin = 1;
990 end = length - 1;
992 while (begin < end) {
993 while (begin < end && compare (&array[0], &array[begin], user_data) > 0)
994 begin++;
995 while (end > begin && compare (&array[0], &array[end], user_data) <= 0)
996 end--;
997 if (begin < end)
998 swfdec_as_array_swap (array, begin, end);
1001 // swap the first element to it's place
1002 while (begin > 0 && compare (&array[0], &array[begin], user_data) <= 0)
1003 begin--;
1004 swfdec_as_array_swap (array, 0, begin);
1006 swfdec_as_array_sort_array (array, begin, compare, user_data);
1007 swfdec_as_array_sort_array (array + begin + 1, length - begin - 1, compare,
1008 user_data);
1011 typedef struct {
1012 SortEntry * array;
1013 gint32 length;
1014 } SortCollectData;
1016 static gboolean
1017 swfdec_as_array_foreach_sort_collect (SwfdecAsObject *object,
1018 const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
1020 SortCollectData *collect_data = data;
1021 gint32 index_;
1023 index_ = swfdec_as_array_to_index (variable);
1024 if (index_ == -1 || index_ >= collect_data->length)
1025 return TRUE;
1027 if (SWFDEC_AS_VALUE_IS_UNDEFINED (*value))
1028 return TRUE;
1030 collect_data->array[index_].value = *value;
1032 return TRUE;
1035 static void
1036 swfdec_as_array_do_sort (SwfdecAsContext *cx, SwfdecAsObject *object,
1037 const SortOption *options, SwfdecAsFunction *custom_function,
1038 const char **fields, SwfdecAsValue *ret)
1040 SortEntry *array;
1041 SortCollectData collect_data;
1042 SortCompareData compare_data;
1043 gint32 i, length;
1044 const char *var;
1045 SwfdecAsObject *target;
1046 SwfdecAsValue val;
1047 SortOption options_;
1048 gboolean descending;
1050 g_return_if_fail (SWFDEC_IS_AS_CONTEXT (cx));
1051 g_return_if_fail (object != NULL);
1052 g_return_if_fail (options != NULL);
1053 g_return_if_fail (custom_function == NULL ||
1054 SWFDEC_IS_AS_FUNCTION (custom_function));
1055 g_return_if_fail (fields == NULL || fields[0] != NULL);
1057 length = swfdec_as_array_get_length (object);
1058 if (length == 0) {
1059 // special case for empty array, because g_try_new0 would return NULL
1060 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1061 return;
1064 if (!swfdec_as_context_try_use_mem (cx, sizeof (SortEntry) * length)) {
1065 SWFDEC_WARNING ("Array not sorted, too big (%i elements)", length);
1066 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1067 return;
1070 // FIXME: this should be different, but context's memory management is not
1071 // done properly yet
1072 array = g_try_new0 (SortEntry, length);
1073 if (!array) {
1074 SWFDEC_WARNING ("Array not sorted, too big (%i elements)", length);
1075 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1076 goto done;
1079 for (i = 0; i < length; i++) {
1080 array[i].index_ = i;
1083 // collect values and indexes to the array
1084 collect_data.length = length;
1085 collect_data.array = array;
1087 swfdec_as_object_foreach (object, swfdec_as_array_foreach_sort_collect,
1088 &collect_data);
1090 // sort the array
1091 compare_data.context = cx;
1092 compare_data.fields = fields;
1093 compare_data.options = options;
1094 // if no fields, then we'll do descending here after the sort
1095 if (fields == NULL && options[0] & SORT_OPTION_DESCENDING) {
1096 descending = TRUE;
1097 options_ = options[0] & ~SORT_OPTION_DESCENDING;
1098 compare_data.options = &options_;
1099 } else {
1100 descending = FALSE;
1102 compare_data.custom_function = custom_function;
1103 compare_data.equal_found = FALSE;
1105 swfdec_as_array_sort_array (array, length, swfdec_as_array_sort_compare,
1106 &compare_data);
1108 // check unique sort
1109 if (options[0] & SORT_OPTION_UNIQUESORT) {
1110 if (fields == NULL && custom_function != NULL) {
1111 // if we used custom_function for comparision, we shall now go trough the
1112 // sorted array and compare them using the default array sort comparision
1113 // and use that to decide if it's unique (no it doesn't make too much
1114 // sense)
1115 for (i = 0; i < length - 1; i++) {
1116 SwfdecAsValue *a = &array[i].value;
1117 SwfdecAsValue *b = &array[i + 1].value;
1118 if (swfdec_as_array_sort_compare_values (cx, a, b, 0, NULL) == 0)
1119 break;
1121 if (i < length - 1) {
1122 *ret = swfdec_as_value_from_integer (cx, 0);
1123 goto done;
1125 } else if (compare_data.equal_found) {
1126 *ret = swfdec_as_value_from_integer (cx, 0);
1127 goto done;
1131 if (options[0] & SORT_OPTION_RETURNINDEXEDARRAY) {
1132 target = swfdec_as_array_new (cx);
1133 } else {
1134 target = object;
1137 for (i = 0; i < length; i++) {
1138 SortEntry *entry = &array[i];
1140 // set only the values that have new indexes
1141 if (!(options[0] & SORT_OPTION_RETURNINDEXEDARRAY) &&
1142 entry->index_ == (descending ? length - i - 1 : i))
1143 continue;
1145 var = swfdec_as_integer_to_string (cx, (descending ? length - i - 1 : i));
1146 if (options[0] & SORT_OPTION_RETURNINDEXEDARRAY) {
1147 val = swfdec_as_value_from_integer (cx, entry->index_);
1148 swfdec_as_object_set_variable (target, var, &val);
1149 } else {
1150 swfdec_as_object_set_variable (target, var, &entry->value);
1154 SWFDEC_AS_VALUE_SET_OBJECT (ret, target);
1156 done:
1157 g_free (array);
1158 swfdec_as_context_unuse_mem (cx, sizeof (SortEntry) * length);
1161 SWFDEC_AS_NATIVE (252, 10, swfdec_as_array_sort)
1162 void
1163 swfdec_as_array_sort (SwfdecAsContext *cx, SwfdecAsObject *object, guint argc,
1164 SwfdecAsValue *argv, SwfdecAsValue *ret)
1166 guint pos;
1167 SortOption options;
1168 SwfdecAsFunction *custom_function;
1170 if (object == NULL || object->movie)
1171 return;
1173 pos = 0;
1175 if (argc > pos && !SWFDEC_AS_VALUE_IS_NUMBER (argv[pos])) {
1176 SwfdecAsFunction *fun;
1177 if (!SWFDEC_AS_VALUE_IS_OBJECT (argv[pos]) ||
1178 !SWFDEC_IS_AS_FUNCTION (
1179 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (argv[pos])->relay))
1180 return;
1181 custom_function = fun;
1182 pos++;
1183 } else {
1184 custom_function = NULL;
1187 if (argc > pos) {
1188 options = swfdec_as_value_to_integer (cx, argv[pos]) & MASK_SORT_OPTION;
1189 } else {
1190 options = 0;
1193 swfdec_as_array_do_sort (cx, object, &options, custom_function, NULL, ret);
1196 SWFDEC_AS_NATIVE (252, 12, swfdec_as_array_sortOn)
1197 void
1198 swfdec_as_array_sortOn (SwfdecAsContext *cx, SwfdecAsObject *object,
1199 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
1201 const char **fields;
1202 SortOption *options;
1203 gint32 i, num_fields;
1204 SwfdecAsObject *array;
1205 SwfdecAsValue val;
1207 if (object == NULL || object->movie)
1208 return;
1210 if (argc < 1)
1211 return;
1213 if (SWFDEC_AS_VALUE_IS_OBJECT (argv[0])) {
1215 array = SWFDEC_AS_VALUE_GET_OBJECT (argv[0]);
1216 if (!array->array) {
1217 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1218 return;
1221 num_fields = swfdec_as_array_get_length (array);
1222 if (num_fields <= 0) {
1223 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
1224 return;
1227 fields = g_new (const char *, num_fields + 1);
1228 for (i = 0; i < num_fields; i++) {
1229 swfdec_as_array_get_value (array, i, &val);
1230 fields[i] = swfdec_as_value_to_string (cx, val);
1233 fields[i] = NULL;
1234 } else {
1235 if (SWFDEC_AS_VALUE_IS_MOVIE (argv[0])) {
1236 SWFDEC_FIXME ("how do we treat movies here?");
1238 num_fields = 1;
1239 fields = g_new (const char *, num_fields + 1);
1240 fields[0] = swfdec_as_value_to_string (cx, argv[0]);
1241 fields[1] = NULL;
1244 options = g_new0 (SortOption, num_fields);
1246 if (argc > 1) {
1247 if (SWFDEC_AS_VALUE_IS_OBJECT (argv[1])) {
1248 array = SWFDEC_AS_VALUE_GET_OBJECT (argv[1]);
1250 if (array->array &&
1251 swfdec_as_array_get_length (array) == num_fields) {
1252 for (i = 0; i < num_fields; i++) {
1253 swfdec_as_array_get_value (array, i, &val);
1254 options[i] =
1255 swfdec_as_value_to_integer (cx, val) & MASK_SORT_OPTION;
1258 } else {
1259 options[0] =
1260 swfdec_as_value_to_integer (cx, argv[1]) & MASK_SORT_OPTION;
1261 for (i = 1; i < num_fields; i++) {
1262 options[i] = options[0];
1267 swfdec_as_array_do_sort (cx, object, options, NULL, fields, ret);
1269 g_free (fields);
1270 g_free (options);
1273 // Constructor
1275 SWFDEC_AS_NATIVE (252, 0, swfdec_as_array_construct)
1276 void
1277 swfdec_as_array_construct (SwfdecAsContext *cx, SwfdecAsObject *object,
1278 guint argc, SwfdecAsValue *argv, SwfdecAsValue *ret)
1280 if (!swfdec_as_context_is_constructing (cx)) {
1281 object = swfdec_as_object_new (cx, NULL);
1282 swfdec_as_object_set_constructor_by_name (object, SWFDEC_AS_STR_Array, NULL);
1284 swfdec_as_object_set_relay (object, NULL);
1285 object->array = TRUE;
1287 if (argc == 1 && SWFDEC_AS_VALUE_IS_NUMBER (argv[0])) {
1288 int l = swfdec_as_value_to_integer (cx, argv[0]);
1289 swfdec_as_array_set_length (object, l < 0 ? 0 : l);
1290 } else if (argc > 0) {
1291 swfdec_as_array_append (object, argc, argv);
1292 } else {
1293 swfdec_as_array_set_length (object, 0);
1296 SWFDEC_AS_VALUE_SET_OBJECT (ret, object);