2 * "$Id: array.c 9772 2011-05-12 05:46:30Z mike $"
4 * Sorted array routines for CUPS.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * cupsArrayAdd() - Add an element to the array.
20 * _cupsArrayAddStrings() - Add zero or more comma-delimited strings to an
22 * cupsArrayClear() - Clear the array.
23 * cupsArrayCount() - Get the number of elements in the array.
24 * cupsArrayCurrent() - Return the current element in the array.
25 * cupsArrayDelete() - Free all memory used by the array.
26 * cupsArrayDup() - Duplicate the array.
27 * cupsArrayFind() - Find an element in the array.
28 * cupsArrayFirst() - Get the first element in the array.
29 * cupsArrayGetIndex() - Get the index of the current element.
30 * cupsArrayGetInsert() - Get the index of the last inserted element.
31 * cupsArrayIndex() - Get the N-th element in the array.
32 * cupsArrayInsert() - Insert an element in the array.
33 * cupsArrayLast() - Get the last element in the array.
34 * cupsArrayNew() - Create a new array.
35 * cupsArrayNew2() - Create a new array with hash.
36 * cupsArrayNew3() - Create a new array with hash and/or free function.
37 * _cupsArrayNewStrings() - Create a new array of comma-delimited strings.
38 * cupsArrayNext() - Get the next element in the array.
39 * cupsArrayPrev() - Get the previous element in the array.
40 * cupsArrayRemove() - Remove an element from the array.
41 * cupsArrayRestore() - Reset the current element to the last @link
43 * cupsArraySave() - Mark the current element for a later @link
45 * cupsArrayUserData() - Return the user data for an array.
46 * cups_array_add() - Insert or append an element to the array.
47 * cups_array_find() - Find an element in the array.
51 * Include necessary headers...
54 #include "string-private.h"
55 #include "debug-private.h"
56 #include "array-private.h"
63 #define _CUPS_MAXSAVE 32 /**** Maximum number of saves ****/
67 * Types and structures...
70 struct _cups_array_s /**** CUPS array structure ****/
73 * The current implementation uses an insertion sort into an array of
74 * sorted pointers. We leave the array type private/opaque so that we
75 * can change the underlying implementation without affecting the users
79 int num_elements, /* Number of array elements */
80 alloc_elements, /* Allocated array elements */
81 current, /* Current element */
82 insert, /* Last inserted element */
83 unique, /* Are all elements unique? */
84 num_saved, /* Number of saved elements */
87 void **elements; /* Array elements */
88 cups_array_func_t compare; /* Element comparison function */
89 void *data; /* User data passed to compare */
90 cups_ahash_func_t hashfunc; /* Hash function */
91 int hashsize, /* Size of hash */
92 *hash; /* Hash array */
93 cups_acopy_func_t copyfunc; /* Copy function */
94 cups_afree_func_t freefunc; /* Free function */
102 static int cups_array_add(cups_array_t *a, void *e, int insert);
103 static int cups_array_find(cups_array_t *a, void *e, int prev, int *rdiff);
107 * 'cupsArrayAdd()' - Add an element to the array.
109 * When adding an element to a sorted array, non-unique elements are
110 * appended at the end of the run of identical elements. For unsorted arrays,
111 * the element is appended to the end of the array.
113 * @since CUPS 1.2/Mac OS X 10.5@
116 int /* O - 1 on success, 0 on failure */
117 cupsArrayAdd(cups_array_t *a, /* I - Array */
118 void *e) /* I - Element */
120 DEBUG_printf(("2cupsArrayAdd(a=%p, e=%p)", a, e));
123 * Range check input...
128 DEBUG_puts("3cupsArrayAdd: returning 0");
133 * Append the element...
136 return (cups_array_add(a, e, 0));
141 * '_cupsArrayAddStrings()' - Add zero or more comma-delimited strings to an
144 * Note: The array MUST be created using the @link _cupsArrayNewStrings@
145 * function. Duplicate strings are NOT added. If the string pointer "s" is NULL
146 * or the empty string, no strings are added to the array.
149 int /* O - 1 on success, 0 on failure */
150 _cupsArrayAddStrings(cups_array_t *a, /* I - Array */
151 const char *s) /* I - Comma-delimited strings or NULL */
153 char *buffer, /* Copy of string */
154 *start, /* Start of string */
155 *end; /* End of string */
156 int status = 1; /* Status of add */
165 * String doesn't contain a comma, so add it as a single value...
168 if (!cupsArrayFind(a, (void *)s))
169 status = cupsArrayAdd(a, (void *)s);
171 else if ((buffer = strdup(s)) == NULL)
175 for (start = end = buffer; *end; start = end)
178 * Find the end of the current delimited string and see if we need to add
182 if ((end = strchr(start, ',')) != NULL)
185 end = start + strlen(start);
187 if (!cupsArrayFind(a, start))
188 status &= cupsArrayAdd(a, start);
199 * 'cupsArrayClear()' - Clear the array.
201 * This function is equivalent to removing all elements in the array.
202 * The caller is responsible for freeing the memory used by the
203 * elements themselves.
205 * @since CUPS 1.2/Mac OS X 10.5@
209 cupsArrayClear(cups_array_t *a) /* I - Array */
212 * Range check input...
219 * Free the existing elements as needed..
224 int i; /* Looping var */
225 void **e; /* Current element */
227 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
228 (a->freefunc)(*e, a->data);
232 * Set the number of elements to 0; we don't actually free the memory
233 * here - that is done in cupsArrayDelete()...
245 * 'cupsArrayCount()' - Get the number of elements in the array.
247 * @since CUPS 1.2/Mac OS X 10.5@
250 int /* O - Number of elements */
251 cupsArrayCount(cups_array_t *a) /* I - Array */
254 * Range check input...
261 * Return the number of elements...
264 return (a->num_elements);
269 * 'cupsArrayCurrent()' - Return the current element in the array.
271 * The current element is undefined until you call @link cupsArrayFind@,
272 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
274 * @since CUPS 1.2/Mac OS X 10.5@
277 void * /* O - Element */
278 cupsArrayCurrent(cups_array_t *a) /* I - Array */
281 * Range check input...
288 * Return the current element...
291 if (a->current >= 0 && a->current < a->num_elements)
292 return (a->elements[a->current]);
299 * 'cupsArrayDelete()' - Free all memory used by the array.
301 * The caller is responsible for freeing the memory used by the
302 * elements themselves.
304 * @since CUPS 1.2/Mac OS X 10.5@
308 cupsArrayDelete(cups_array_t *a) /* I - Array */
311 * Range check input...
318 * Free the elements if we have a free function (otherwise the caller is
319 * responsible for doing the dirty work...)
324 int i; /* Looping var */
325 void **e; /* Current element */
327 for (i = a->num_elements, e = a->elements; i > 0; i --, e ++)
328 (a->freefunc)(*e, a->data);
332 * Free the array of element pointers...
335 if (a->alloc_elements)
346 * 'cupsArrayDup()' - Duplicate the array.
348 * @since CUPS 1.2/Mac OS X 10.5@
351 cups_array_t * /* O - Duplicate array */
352 cupsArrayDup(cups_array_t *a) /* I - Array */
354 cups_array_t *da; /* Duplicate array */
358 * Range check input...
365 * Allocate memory for the array...
368 da = calloc(1, sizeof(cups_array_t));
372 da->compare = a->compare;
374 da->current = a->current;
375 da->insert = a->insert;
376 da->unique = a->unique;
377 da->num_saved = a->num_saved;
379 memcpy(da->saved, a->saved, sizeof(a->saved));
384 * Allocate memory for the elements...
387 da->elements = malloc(a->num_elements * sizeof(void *));
395 * Copy the element pointers...
401 * Use the copy function to make a copy of each element...
404 int i; /* Looping var */
406 for (i = 0; i < a->num_elements; i ++)
407 da->elements[i] = (a->copyfunc)(a->elements[i], a->data);
412 * Just copy raw pointers...
415 memcpy(da->elements, a->elements, a->num_elements * sizeof(void *));
418 da->num_elements = a->num_elements;
419 da->alloc_elements = a->num_elements;
423 * Return the new array...
431 * 'cupsArrayFind()' - Find an element in the array.
433 * @since CUPS 1.2/Mac OS X 10.5@
436 void * /* O - Element found or @code NULL@ */
437 cupsArrayFind(cups_array_t *a, /* I - Array */
438 void *e) /* I - Element */
440 int current, /* Current element */
441 diff, /* Difference */
442 hash; /* Hash index */
446 * Range check input...
453 * See if we have any elements...
456 if (!a->num_elements)
460 * Yes, look for a match...
465 hash = (*(a->hashfunc))(e, a->data);
467 if (hash < 0 || hash >= a->hashsize)
469 current = a->current;
474 current = a->hash[hash];
476 if (current < 0 || current >= a->num_elements)
477 current = a->current;
482 current = a->current;
486 current = cups_array_find(a, e, current, &diff);
490 * Found a match! If the array does not contain unique values, find
491 * the first element that is the same...
494 if (!a->unique && a->compare)
497 * The array is not unique, find the first match...
500 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
505 a->current = current;
508 a->hash[hash] = current;
510 return (a->elements[current]);
526 * 'cupsArrayFirst()' - Get the first element in the array.
528 * @since CUPS 1.2/Mac OS X 10.5@
531 void * /* O - First element or @code NULL@ if the array is empty */
532 cupsArrayFirst(cups_array_t *a) /* I - Array */
535 * Range check input...
542 * Return the first element...
547 return (cupsArrayCurrent(a));
552 * 'cupsArrayGetIndex()' - Get the index of the current element.
554 * The current element is undefined until you call @link cupsArrayFind@,
555 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@.
557 * @since CUPS 1.3/Mac OS X 10.5@
560 int /* O - Index of the current element, starting at 0 */
561 cupsArrayGetIndex(cups_array_t *a) /* I - Array */
571 * 'cupsArrayGetInsert()' - Get the index of the last inserted element.
573 * @since CUPS 1.3/Mac OS X 10.5@
576 int /* O - Index of the last inserted element, starting at 0 */
577 cupsArrayGetInsert(cups_array_t *a) /* I - Array */
587 * 'cupsArrayIndex()' - Get the N-th element in the array.
589 * @since CUPS 1.2/Mac OS X 10.5@
592 void * /* O - N-th element or @code NULL@ */
593 cupsArrayIndex(cups_array_t *a, /* I - Array */
594 int n) /* I - Index into array, starting at 0 */
601 return (cupsArrayCurrent(a));
606 * 'cupsArrayInsert()' - Insert an element in the array.
608 * When inserting an element in a sorted array, non-unique elements are
609 * inserted at the beginning of the run of identical elements. For unsorted
610 * arrays, the element is inserted at the beginning of the array.
612 * @since CUPS 1.2/Mac OS X 10.5@
615 int /* O - 0 on failure, 1 on success */
616 cupsArrayInsert(cups_array_t *a, /* I - Array */
617 void *e) /* I - Element */
619 DEBUG_printf(("2cupsArrayInsert(a=%p, e=%p)", a, e));
622 * Range check input...
627 DEBUG_puts("3cupsArrayInsert: returning 0");
632 * Insert the element...
635 return (cups_array_add(a, e, 1));
640 * 'cupsArrayLast()' - Get the last element in the array.
642 * @since CUPS 1.2/Mac OS X 10.5@
645 void * /* O - Last element or @code NULL@ if the array is empty */
646 cupsArrayLast(cups_array_t *a) /* I - Array */
649 * Range check input...
656 * Return the last element...
659 a->current = a->num_elements - 1;
661 return (cupsArrayCurrent(a));
666 * 'cupsArrayNew()' - Create a new array.
668 * The comparison function ("f") is used to create a sorted array. The function
669 * receives pointers to two elements and the user data pointer ("d") - the user
670 * data pointer argument can safely be omitted when not required so functions
671 * like @code strcmp@ can be used for sorted string arrays.
673 * @since CUPS 1.2/Mac OS X 10.5@
676 cups_array_t * /* O - Array */
677 cupsArrayNew(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
678 void *d) /* I - User data pointer or @code NULL@ */
680 return (cupsArrayNew3(f, d, 0, 0, 0, 0));
685 * 'cupsArrayNew2()' - Create a new array with hash.
687 * The comparison function ("f") is used to create a sorted array. The function
688 * receives pointers to two elements and the user data pointer ("d") - the user
689 * data pointer argument can safely be omitted when not required so functions
690 * like @code strcmp@ can be used for sorted string arrays.
692 * The hash function ("h") is used to implement cached lookups with the
693 * specified hash size ("hsize").
695 * @since CUPS 1.3/Mac OS X 10.5@
698 cups_array_t * /* O - Array */
699 cupsArrayNew2(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
700 void *d, /* I - User data or @code NULL@ */
701 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
702 int hsize) /* I - Hash size (>= 0) */
704 return (cupsArrayNew3(f, d, h, hsize, 0, 0));
709 * 'cupsArrayNew3()' - Create a new array with hash and/or free function.
711 * The comparison function ("f") is used to create a sorted array. The function
712 * receives pointers to two elements and the user data pointer ("d") - the user
713 * data pointer argument can safely be omitted when not required so functions
714 * like @code strcmp@ can be used for sorted string arrays.
716 * The hash function ("h") is used to implement cached lookups with the
717 * specified hash size ("hsize").
719 * The copy function ("cf") is used to automatically copy/retain elements when
720 * added or the array is copied.
722 * The free function ("cf") is used to automatically free/release elements when
723 * removed or the array is deleted.
725 * @since CUPS 1.5/Mac OS X 10.7@
728 cups_array_t * /* O - Array */
729 cupsArrayNew3(cups_array_func_t f, /* I - Comparison function or @code NULL@ for an unsorted array */
730 void *d, /* I - User data or @code NULL@ */
731 cups_ahash_func_t h, /* I - Hash function or @code NULL@ for unhashed lookups */
732 int hsize, /* I - Hash size (>= 0) */
733 cups_acopy_func_t cf, /* I - Copy function */
734 cups_afree_func_t ff) /* I - Free function */
736 cups_array_t *a; /* Array */
740 * Allocate memory for the array...
743 a = calloc(1, sizeof(cups_array_t));
758 a->hash = malloc(hsize * sizeof(int));
766 memset(a->hash, -1, hsize * sizeof(int));
777 * '_cupsArrayNewStrings()' - Create a new array of comma-delimited strings.
779 * Note: The array automatically manages copies of the strings passed. If the
780 * string pointer "s" is NULL or the empty string, no strings are added to the
781 * newly created array.
784 cups_array_t * /* O - Array */
785 _cupsArrayNewStrings(const char *s) /* I - Comma-delimited strings or NULL */
787 cups_array_t *a; /* Array */
790 if ((a = cupsArrayNew3((cups_array_func_t)strcmp, NULL, NULL, 0,
791 (cups_acopy_func_t)_cupsStrAlloc,
792 (cups_afree_func_t)_cupsStrFree)) != NULL)
793 _cupsArrayAddStrings(a, s);
800 * 'cupsArrayNext()' - Get the next element in the array.
802 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) + 1)".
804 * The next element is undefined until you call @link cupsArrayFind@,
805 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
806 * to set the current element.
808 * @since CUPS 1.2/Mac OS X 10.5@
811 void * /* O - Next element or @code NULL@ */
812 cupsArrayNext(cups_array_t *a) /* I - Array */
815 * Range check input...
822 * Return the next element...
825 if (a->current < a->num_elements)
828 return (cupsArrayCurrent(a));
833 * 'cupsArrayPrev()' - Get the previous element in the array.
835 * This function is equivalent to "cupsArrayIndex(a, cupsArrayGetIndex(a) - 1)".
837 * The previous element is undefined until you call @link cupsArrayFind@,
838 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
839 * to set the current element.
841 * @since CUPS 1.2/Mac OS X 10.5@
844 void * /* O - Previous element or @code NULL@ */
845 cupsArrayPrev(cups_array_t *a) /* I - Array */
848 * Range check input...
855 * Return the previous element...
861 return (cupsArrayCurrent(a));
866 * 'cupsArrayRemove()' - Remove an element from the array.
868 * If more than one element matches "e", only the first matching element is
871 * The caller is responsible for freeing the memory used by the
874 * @since CUPS 1.2/Mac OS X 10.5@
877 int /* O - 1 on success, 0 on failure */
878 cupsArrayRemove(cups_array_t *a, /* I - Array */
879 void *e) /* I - Element */
881 int i, /* Looping var */
882 current, /* Current element */
883 diff; /* Difference */
887 * Range check input...
894 * See if the element is in the array...
897 if (!a->num_elements)
900 current = cups_array_find(a, e, a->current, &diff);
905 * Yes, now remove it...
911 (a->freefunc)(a->elements[current], a->data);
913 if (current < a->num_elements)
914 memmove(a->elements + current, a->elements + current + 1,
915 (a->num_elements - current) * sizeof(void *));
917 if (current <= a->current)
920 if (current < a->insert)
922 else if (current == a->insert)
925 for (i = 0; i < a->num_saved; i ++)
926 if (current <= a->saved[i])
929 if (a->num_elements <= 1)
937 * 'cupsArrayRestore()' - Reset the current element to the last @link cupsArraySave@.
939 * @since CUPS 1.2/Mac OS X 10.5@
942 void * /* O - New current element */
943 cupsArrayRestore(cups_array_t *a) /* I - Array */
948 if (a->num_saved <= 0)
952 a->current = a->saved[a->num_saved];
954 if (a->current >= 0 && a->current < a->num_elements)
955 return (a->elements[a->current]);
962 * 'cupsArraySave()' - Mark the current element for a later @link cupsArrayRestore@.
964 * The current element is undefined until you call @link cupsArrayFind@,
965 * @link cupsArrayFirst@, or @link cupsArrayIndex@, or @link cupsArrayLast@
966 * to set the current element.
968 * The save/restore stack is guaranteed to be at least 32 elements deep.
970 * @since CUPS 1.2/Mac OS X 10.5@
973 int /* O - 1 on success, 0 on failure */
974 cupsArraySave(cups_array_t *a) /* I - Array */
979 if (a->num_saved >= _CUPS_MAXSAVE)
982 a->saved[a->num_saved] = a->current;
990 * 'cupsArrayUserData()' - Return the user data for an array.
992 * @since CUPS 1.2/Mac OS X 10.5@
995 void * /* O - User data */
996 cupsArrayUserData(cups_array_t *a) /* I - Array */
1006 * 'cups_array_add()' - Insert or append an element to the array.
1008 * @since CUPS 1.2/Mac OS X 10.5@
1011 static int /* O - 1 on success, 0 on failure */
1012 cups_array_add(cups_array_t *a, /* I - Array */
1013 void *e, /* I - Element to add */
1014 int insert) /* I - 1 = insert, 0 = append */
1016 int i, /* Looping var */
1017 current, /* Current element */
1018 diff; /* Comparison with current element */
1021 DEBUG_printf(("7cups_array_add(a=%p, e=%p, insert=%d)", a, e, insert));
1024 * Verify we have room for the new element...
1027 if (a->num_elements >= a->alloc_elements)
1030 * Allocate additional elements; start with 16 elements, then
1031 * double the size until 1024 elements, then add 1024 elements
1035 void **temp; /* New array elements */
1036 int count; /* New allocation count */
1039 if (a->alloc_elements == 0)
1042 temp = malloc(count * sizeof(void *));
1046 if (a->alloc_elements < 1024)
1047 count = a->alloc_elements * 2;
1049 count = a->alloc_elements + 1024;
1051 temp = realloc(a->elements, count * sizeof(void *));
1054 DEBUG_printf(("9cups_array_add: count=%d", count));
1058 DEBUG_puts("9cups_array_add: allocation failed, returning 0");
1062 a->alloc_elements = count;
1067 * Find the insertion point for the new element; if there is no
1068 * compare function or elements, just add it to the beginning or end...
1071 if (!a->num_elements || !a->compare)
1074 * No elements or comparison function, insert/append as needed...
1078 current = 0; /* Insert at beginning */
1080 current = a->num_elements; /* Append to the end */
1085 * Do a binary search for the insertion point...
1088 current = cups_array_find(a, e, a->insert, &diff);
1093 * Insert after the current element...
1101 * Compared equal, make sure we add to the begining or end of
1102 * the current run of equal elements...
1110 * Insert at beginning of run...
1113 while (current > 0 && !(*(a->compare))(e, a->elements[current - 1],
1120 * Append at end of run...
1127 while (current < a->num_elements &&
1128 !(*(a->compare))(e, a->elements[current], a->data));
1134 * Insert or append the element...
1137 if (current < a->num_elements)
1140 * Shift other elements to the right...
1143 memmove(a->elements + current + 1, a->elements + current,
1144 (a->num_elements - current) * sizeof(void *));
1146 if (a->current >= current)
1149 for (i = 0; i < a->num_saved; i ++)
1150 if (a->saved[i] >= current)
1153 DEBUG_printf(("9cups_array_add: insert element at index %d...", current));
1157 DEBUG_printf(("9cups_array_add: append element at %d...", current));
1162 if ((a->elements[current] = (a->copyfunc)(e, a->data)) == NULL)
1164 DEBUG_puts("8cups_array_add: Copy function returned NULL, returning 0");
1169 a->elements[current] = e;
1172 a->insert = current;
1175 for (current = 0; current < a->num_elements; current ++)
1176 DEBUG_printf(("9cups_array_add: a->elements[%d]=%p", current,
1177 a->elements[current]));
1180 DEBUG_puts("9cups_array_add: returning 1");
1187 * 'cups_array_find()' - Find an element in the array.
1190 static int /* O - Index of match */
1191 cups_array_find(cups_array_t *a, /* I - Array */
1192 void *e, /* I - Element */
1193 int prev, /* I - Previous index */
1194 int *rdiff) /* O - Difference of match */
1196 int left, /* Left side of search */
1197 right, /* Right side of search */
1198 current, /* Current element */
1199 diff; /* Comparison with current element */
1202 DEBUG_printf(("7cups_array_find(a=%p, e=%p, prev=%d, rdiff=%p)", a, e, prev,
1208 * Do a binary search for the element...
1211 DEBUG_puts("9cups_array_find: binary search");
1213 if (prev >= 0 && prev < a->num_elements)
1216 * Start search on either side of previous...
1219 if ((diff = (*(a->compare))(e, a->elements[prev], a->data)) == 0 ||
1220 (diff < 0 && prev == 0) ||
1221 (diff > 0 && prev == (a->num_elements - 1)))
1224 * Exact or edge match, return it!
1227 DEBUG_printf(("9cups_array_find: Returning %d, diff=%d", prev, diff));
1236 * Start with previous on right side...
1245 * Start wih previous on left side...
1249 right = a->num_elements - 1;
1255 * Start search in the middle...
1259 right = a->num_elements - 1;
1264 current = (left + right) / 2;
1265 diff = (*(a->compare))(e, a->elements[current], a->data);
1267 DEBUG_printf(("9cups_array_find: left=%d, right=%d, current=%d, diff=%d",
1268 left, right, current, diff));
1277 while ((right - left) > 1);
1282 * Check the last 1 or 2 elements...
1285 if ((diff = (*(a->compare))(e, a->elements[left], a->data)) <= 0)
1289 diff = (*(a->compare))(e, a->elements[right], a->data);
1297 * Do a linear pointer search...
1300 DEBUG_puts("9cups_array_find: linear search");
1304 for (current = 0; current < a->num_elements; current ++)
1305 if (a->elements[current] == e)
1313 * Return the closest element and the difference...
1316 DEBUG_printf(("8cups_array_find: Returning %d, diff=%d", current, diff));
1325 * End of "$Id: array.c 9772 2011-05-12 05:46:30Z mike $".