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.
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
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"
43 * @short_description: utility functions for treating objects as arrays
45 * The array object provides some convenience functions for creating and
50 * Internal helper functions
53 /* NB: type is important for overflow */
55 swfdec_as_array_to_index (const char *str
)
60 g_return_val_if_fail (str
!= NULL
, -1);
62 l
= strtoul (str
, &end
, 10);
64 if (*end
!= 0 || l
> G_MAXINT32
)
71 swfdec_as_array_length_as_integer (SwfdecAsObject
*object
)
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
);
85 * swfdec_as_array_get_length:
88 * Gets the current length of the @array.
90 * Returns: Current length of the @array, always >= 0
93 swfdec_as_array_get_length (SwfdecAsObject
*array
)
97 g_return_val_if_fail (array
!= NULL
, 0);
99 length
= swfdec_as_array_length_as_integer (array
);
108 swfdec_as_array_set_length_object (SwfdecAsObject
*object
, gint32 length
)
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:
128 * @length: the new length
130 * Sets the length of the @array. Values outside the new length will be
134 swfdec_as_array_set_length (SwfdecAsObject
*array
, gint32 length
)
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
);
150 } ForeachRemoveRangeData
;
153 swfdec_as_array_foreach_remove_range (SwfdecAsObject
*object
,
154 const char *variable
, SwfdecAsValue
*value
, guint flags
, gpointer data
)
156 ForeachRemoveRangeData
*fdata
= data
;
159 idx
= swfdec_as_array_to_index (variable
);
163 if (flags
& SWFDEC_AS_VARIABLE_PERMANENT
)
166 if (idx
>= fdata
->start_index
&& idx
< fdata
->start_index
+ fdata
->num
)
173 swfdec_as_array_remove_range (SwfdecAsObject
*object
, gint32 start_index
,
176 g_return_if_fail (start_index
>= 0);
177 g_return_if_fail (num
>= 0);
182 // to avoid foreach loop, use special case when removing just one variable
184 swfdec_as_object_delete_variable (object
,
185 swfdec_as_integer_to_string (object
->context
, start_index
));
187 ForeachRemoveRangeData fdata
= { start_index
, num
};
188 swfdec_as_object_foreach_remove (object
,
189 swfdec_as_array_foreach_remove_range
, &fdata
);
197 } ForeachMoveRangeData
;
200 swfdec_as_array_foreach_move_range (SwfdecAsObject
*object
,
201 const char *variable
, SwfdecAsValue
*value
, guint flags
, gpointer data
)
203 ForeachMoveRangeData
*fdata
= data
;
206 idx
= swfdec_as_array_to_index (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
) {
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
)
234 swfdec_as_object_foreach_rename (object
, swfdec_as_array_foreach_move_range
,
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
);
243 swfdec_as_array_set_range_with_flags (SwfdecAsObject
*object
,
244 gint32 start_index
, gint32 num
, const SwfdecAsValue
*value
,
245 SwfdecAsVariableFlag flags
)
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
);
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);
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:
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:
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:
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:
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.
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:
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:
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.
348 swfdec_as_array_insert_with_flags (SwfdecAsObject
*array
, gint32 idx
,
349 const SwfdecAsValue
*value
, SwfdecAsVariableFlag flags
)
353 g_return_if_fail (array
!= NULL
);
354 g_return_if_fail (idx
>= 0);
356 length
= swfdec_as_array_get_length (array
);
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:
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.
372 swfdec_as_array_remove (SwfdecAsObject
*array
, gint32 idx
)
376 g_return_if_fail (array
!= NULL
);
377 g_return_if_fail (idx
>= 0);
379 length
= swfdec_as_array_get_length (array
);
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:
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
398 swfdec_as_array_get_value (SwfdecAsObject
*array
, gint32 idx
,
399 SwfdecAsValue
*value
)
403 g_assert (array
!= NULL
);
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:
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
421 swfdec_as_array_set_value (SwfdecAsObject
*array
, gint32 idx
,
422 SwfdecAsValue
*value
)
426 g_assert (array
!= NULL
);
429 var
= swfdec_as_integer_to_string (array
->context
, idx
);
430 swfdec_as_object_set_variable (array
, var
, value
);
434 SwfdecAsObject
*object_to
;
438 } ForeachAppendArrayRangeData
;
441 swfdec_as_array_foreach_append_array_range (SwfdecAsObject
*object
,
442 const char *variable
, SwfdecAsValue
*value
, guint flags
, gpointer data
)
444 ForeachAppendArrayRangeData
*fdata
= data
;
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
);
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);
471 fdata
.object_to
= array_to
;
472 fdata
.offset
= swfdec_as_array_get_length (array_to
);
473 fdata
.start_index
= start_index
;
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
);
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
));
495 * swfdec_as_array_new:
496 * @context: a #SwfdecAsContext
498 * Creates a new array.
500 * Returns: the new array
503 swfdec_as_array_new (SwfdecAsContext
*context
)
507 g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context
), NULL
);
509 ret
= swfdec_as_object_new (context
, NULL
);
511 swfdec_as_object_set_constructor_by_name (ret
, SWFDEC_AS_STR_Array
, NULL
);
513 swfdec_as_array_set_length_object (ret
, 0);
520 SWFDEC_AS_NATIVE (252, 7, swfdec_as_array_join
)
522 swfdec_as_array_join (SwfdecAsContext
*cx
, SwfdecAsObject
*object
, guint argc
,
523 SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
526 const char *var
, *str
, *sep
;
529 if (object
== NULL
|| object
->movie
)
533 sep
= swfdec_as_value_to_string (cx
, argv
[0]);
535 sep
= SWFDEC_AS_STR_COMMA
;
538 // note: we don't cache length
539 if (swfdec_as_array_get_length (object
) > 0) {
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
));
553 str
= SWFDEC_AS_STR_EMPTY
;
556 SWFDEC_AS_VALUE_SET_STRING (ret
, str
);
559 SWFDEC_AS_NATIVE (252, 9, swfdec_as_array_toString
)
561 swfdec_as_array_toString (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
562 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
564 if (object
== NULL
|| object
->movie
)
567 swfdec_as_array_join (cx
, object
, 0, NULL
, ret
);
570 SWFDEC_AS_NATIVE (252, 1, swfdec_as_array_do_push
)
572 swfdec_as_array_do_push (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
573 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
575 if (object
== NULL
|| object
->movie
)
578 // if 0 args, just return the length
579 // manually set the length here to make the function work on non-Arrays
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
)
591 swfdec_as_array_do_pop (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
592 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
597 if (object
== NULL
|| object
->movie
)
600 // we allow negative indexes here, but not 0
601 length
= swfdec_as_array_length_as_integer (object
);
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
613 swfdec_as_array_set_length_object (object
, length
- 1);
616 SWFDEC_AS_NATIVE (252, 5, swfdec_as_array_do_unshift
)
618 swfdec_as_array_do_unshift (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
619 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
623 if (object
== NULL
|| object
->movie
)
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
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
)
641 swfdec_as_array_do_shift (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
642 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
647 if (object
== NULL
|| object
->movie
)
650 // don't allow negative length
651 length
= swfdec_as_array_get_length (object
);
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
661 swfdec_as_array_set_length_object (object
, length
- 1);
663 // we have to put the last element back, because we used move, not copy
666 var
= swfdec_as_integer_to_string (object
->context
, length
- 2);
667 swfdec_as_object_get_variable (object
, var
, &val
);
671 var
= swfdec_as_integer_to_string (object
->context
, length
- 1);
672 swfdec_as_object_set_variable (object
, var
, &val
);
677 swfdec_as_array_foreach_reverse (SwfdecAsObject
*object
, const char *variable
,
678 SwfdecAsValue
*value
, guint flags
, gpointer data
)
680 gint32
*length
= data
;
683 idx
= swfdec_as_array_to_index (variable
);
684 if (idx
== -1 || idx
>= *length
)
687 return swfdec_as_integer_to_string (object
->context
, *length
- 1 - idx
);
690 SWFDEC_AS_NATIVE (252, 11, swfdec_as_array_reverse
)
692 swfdec_as_array_reverse (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
693 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
697 if (object
== NULL
|| object
->movie
)
700 length
= swfdec_as_array_get_length (object
);
701 swfdec_as_object_foreach_rename (object
, swfdec_as_array_foreach_reverse
,
704 SWFDEC_AS_VALUE_SET_OBJECT (ret
, object
);
707 SWFDEC_AS_NATIVE (252, 3, swfdec_as_array_concat
)
709 swfdec_as_array_concat (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
710 guint argc
, SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
713 SwfdecAsObject
*array_new
;
716 if (object
== NULL
|| object
->movie
)
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
]));
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
)
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
)
751 length
= swfdec_as_array_get_length (object
);
754 start_index
= swfdec_as_value_to_integer (cx
, argv
[0]);
756 start_index
= length
+ start_index
;
757 start_index
= CLAMP (start_index
, 0, length
);
763 gint32 endIndex
= swfdec_as_value_to_integer (cx
, argv
[1]);
765 endIndex
= length
+ endIndex
;
766 endIndex
= CLAMP (endIndex
, start_index
, length
);
767 num
= endIndex
- start_index
;
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
)
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)
790 length
= swfdec_as_array_get_length (object
);
792 start_index
= swfdec_as_value_to_integer (cx
, argv
[0]);
794 start_index
= length
+ start_index
;
795 start_index
= CLAMP (start_index
, 0, length
);
798 int tmp
= swfdec_as_value_to_integer (cx
, argv
[1]);
801 num_remove
= MIN (tmp
, length
- start_index
);
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
,
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
));
827 swfdec_as_array_set_range (object
, start_index
, argc
- 2, argv
+ 2);
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
839 #define MASK_SORT_OPTION ((1 << 5) - 1)
846 // inner function for swfdec_as_array_sort_compare
848 swfdec_as_array_sort_compare_values (SwfdecAsContext
*cx
,
849 const SwfdecAsValue
*a
, const SwfdecAsValue
*b
, SortOption options
,
850 SwfdecAsFunction
*custom_function
)
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
};
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
)) {
873 } else if (!SWFDEC_AS_VALUE_IS_NUMBER (*b
)) {
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));
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
);
891 retval
= strcmp (a_str
, b_str
);
895 if (options
& SORT_OPTION_DESCENDING
)
902 SwfdecAsContext
* context
;
903 const char ** fields
;
904 const SortOption
* options
;
905 SwfdecAsFunction
* custom_function
;
906 gboolean equal_found
;
910 swfdec_as_array_sort_compare (gconstpointer a_ptr
, gconstpointer b_ptr
,
913 const SwfdecAsValue
*a
= &((SortEntry
*)a_ptr
)->value
;
914 const SwfdecAsValue
*b
= &((SortEntry
*)b_ptr
)->value
;
915 SortCompareData
*data
= user_data
;
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
);
928 SwfdecAsValue a_comp
, b_comp
;
929 SwfdecAsObject
*object
;
934 object
= swfdec_as_value_to_object (data
->context
, *a
);
936 swfdec_as_object_get_variable (object
, data
->fields
[i
], &a_comp
);
938 SWFDEC_AS_VALUE_SET_UNDEFINED (&a_comp
);
941 object
= swfdec_as_value_to_object (data
->context
, *b
);
943 swfdec_as_object_get_variable (object
, data
->fields
[i
], &b_comp
);
945 SWFDEC_AS_VALUE_SET_UNDEFINED (&b_comp
);
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
);
955 data
->equal_found
= TRUE
;
961 swfdec_as_array_swap (SortEntry
*array
, gint32 a
, gint32 b
)
965 g_return_if_fail (array
!= NULL
);
966 g_return_if_fail (a
>= 0);
967 g_return_if_fail (b
>= 0);
974 // what an awesome function name...
976 swfdec_as_array_sort_array (SortEntry
*array
, gint32 length
,
977 GCompareDataFunc compare
, gpointer user_data
)
981 g_return_if_fail (array
!= NULL
);
982 g_return_if_fail (length
>= 0);
983 g_return_if_fail (compare
!= NULL
);
988 // swap the first bigger element with the last smaller element
992 while (begin
< end
) {
993 while (begin
< end
&& compare (&array
[0], &array
[begin
], user_data
) > 0)
995 while (end
> begin
&& compare (&array
[0], &array
[end
], user_data
) <= 0)
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)
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
,
1017 swfdec_as_array_foreach_sort_collect (SwfdecAsObject
*object
,
1018 const char *variable
, SwfdecAsValue
*value
, guint flags
, gpointer data
)
1020 SortCollectData
*collect_data
= data
;
1023 index_
= swfdec_as_array_to_index (variable
);
1024 if (index_
== -1 || index_
>= collect_data
->length
)
1027 if (SWFDEC_AS_VALUE_IS_UNDEFINED (*value
))
1030 collect_data
->array
[index_
].value
= *value
;
1036 swfdec_as_array_do_sort (SwfdecAsContext
*cx
, SwfdecAsObject
*object
,
1037 const SortOption
*options
, SwfdecAsFunction
*custom_function
,
1038 const char **fields
, SwfdecAsValue
*ret
)
1041 SortCollectData collect_data
;
1042 SortCompareData compare_data
;
1045 SwfdecAsObject
*target
;
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
);
1059 // special case for empty array, because g_try_new0 would return NULL
1060 SWFDEC_AS_VALUE_SET_OBJECT (ret
, object
);
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
);
1070 // FIXME: this should be different, but context's memory management is not
1071 // done properly yet
1072 array
= g_try_new0 (SortEntry
, length
);
1074 SWFDEC_WARNING ("Array not sorted, too big (%i elements)", length
);
1075 SWFDEC_AS_VALUE_SET_OBJECT (ret
, object
);
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
,
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
) {
1097 options_
= options
[0] & ~SORT_OPTION_DESCENDING
;
1098 compare_data
.options
= &options_
;
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
,
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
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)
1121 if (i
< length
- 1) {
1122 *ret
= swfdec_as_value_from_integer (cx
, 0);
1125 } else if (compare_data
.equal_found
) {
1126 *ret
= swfdec_as_value_from_integer (cx
, 0);
1131 if (options
[0] & SORT_OPTION_RETURNINDEXEDARRAY
) {
1132 target
= swfdec_as_array_new (cx
);
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
))
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
);
1150 swfdec_as_object_set_variable (target
, var
, &entry
->value
);
1154 SWFDEC_AS_VALUE_SET_OBJECT (ret
, target
);
1158 swfdec_as_context_unuse_mem (cx
, sizeof (SortEntry
) * length
);
1161 SWFDEC_AS_NATIVE (252, 10, swfdec_as_array_sort
)
1163 swfdec_as_array_sort (SwfdecAsContext
*cx
, SwfdecAsObject
*object
, guint argc
,
1164 SwfdecAsValue
*argv
, SwfdecAsValue
*ret
)
1168 SwfdecAsFunction
*custom_function
;
1170 if (object
== NULL
|| object
->movie
)
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
))
1181 custom_function
= fun
;
1184 custom_function
= NULL
;
1188 options
= swfdec_as_value_to_integer (cx
, argv
[pos
]) & MASK_SORT_OPTION
;
1193 swfdec_as_array_do_sort (cx
, object
, &options
, custom_function
, NULL
, ret
);
1196 SWFDEC_AS_NATIVE (252, 12, swfdec_as_array_sortOn
)
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
;
1207 if (object
== NULL
|| object
->movie
)
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
);
1221 num_fields
= swfdec_as_array_get_length (array
);
1222 if (num_fields
<= 0) {
1223 SWFDEC_AS_VALUE_SET_OBJECT (ret
, object
);
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
);
1235 if (SWFDEC_AS_VALUE_IS_MOVIE (argv
[0])) {
1236 SWFDEC_FIXME ("how do we treat movies here?");
1239 fields
= g_new (const char *, num_fields
+ 1);
1240 fields
[0] = swfdec_as_value_to_string (cx
, argv
[0]);
1244 options
= g_new0 (SortOption
, num_fields
);
1247 if (SWFDEC_AS_VALUE_IS_OBJECT (argv
[1])) {
1248 array
= SWFDEC_AS_VALUE_GET_OBJECT (argv
[1]);
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
);
1255 swfdec_as_value_to_integer (cx
, val
) & MASK_SORT_OPTION
;
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
);
1275 SWFDEC_AS_NATIVE (252, 0, swfdec_as_array_construct
)
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
);
1293 swfdec_as_array_set_length (object
, 0);
1296 SWFDEC_AS_VALUE_SET_OBJECT (ret
, object
);