| 802 |
812 |
| 803 void cxListFree(CxList *list) { |
813 void cxListFree(CxList *list) { |
| 804 if (list == NULL) return; |
814 if (list == NULL) return; |
| 805 list->cl->deallocate(list); |
815 list->cl->deallocate(list); |
| 806 } |
816 } |
| |
817 |
| |
818 static void cx_list_pop_uninitialized_elements(CxList *list, size_t n) { |
| |
819 cx_destructor_func destr_bak = list->collection.simple_destructor; |
| |
820 cx_destructor_func2 destr2_bak = list->collection.advanced_destructor; |
| |
821 list->collection.simple_destructor = NULL; |
| |
822 list->collection.advanced_destructor = NULL; |
| |
823 if (n == 1) { |
| |
824 cxListRemove(list, list->collection.size - 1); |
| |
825 } else { |
| |
826 cxListRemoveArray(list,list->collection.size - n, n); |
| |
827 } |
| |
828 list->collection.simple_destructor = destr_bak; |
| |
829 list->collection.advanced_destructor = destr2_bak; |
| |
830 } |
| |
831 |
| |
832 static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { |
| |
833 size_t elem_size = *(size_t*)data; |
| |
834 if (dst == NULL) dst = cxMalloc(al, elem_size); |
| |
835 if (dst != NULL) memcpy(dst, src, elem_size); |
| |
836 return dst; |
| |
837 } |
| |
838 |
| |
839 #define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size) |
| |
840 |
| |
841 int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func, |
| |
842 const CxAllocator *clone_allocator, void *data) { |
| |
843 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
844 |
| |
845 // remember the original size |
| |
846 size_t orig_size = dst->collection.size; |
| |
847 |
| |
848 // first, try to allocate the memory in the new list |
| |
849 CxIterator empl_iter = cxListEmplaceArray(dst, src->collection.size); |
| |
850 |
| |
851 // get an iterator over the source elements |
| |
852 CxIterator src_iter = cxListIterator(src); |
| |
853 |
| |
854 // now clone the elements |
| |
855 size_t cloned = empl_iter.elem_count; |
| |
856 for (size_t i = 0 ; i < empl_iter.elem_count; i++) { |
| |
857 void *src_elem = cxIteratorCurrent(src_iter); |
| |
858 void **dest_memory = cxIteratorCurrent(empl_iter); |
| |
859 void *target = cxCollectionStoresPointers(dst) ? NULL : dest_memory; |
| |
860 void *dest_ptr = clone_func(target, src_elem, clone_allocator, data); |
| |
861 if (dest_ptr == NULL) { |
| |
862 cloned = i; |
| |
863 break; |
| |
864 } |
| |
865 if (cxCollectionStoresPointers(dst)) { |
| |
866 *dest_memory = dest_ptr; |
| |
867 } |
| |
868 cxIteratorNext(src_iter); |
| |
869 cxIteratorNext(empl_iter); |
| |
870 } |
| |
871 |
| |
872 // if we could not clone everything, free the allocated memory |
| |
873 // (disable the destructors!) |
| |
874 if (cloned < src->collection.size) { |
| |
875 cx_list_pop_uninitialized_elements(dst, |
| |
876 dst->collection.size - cloned - orig_size); |
| |
877 return 1; |
| |
878 } |
| |
879 |
| |
880 // set the sorted flag when we know it's sorted |
| |
881 if (orig_size == 0 && src->collection.sorted) { |
| |
882 dst->collection.sorted = true; |
| |
883 } |
| |
884 |
| |
885 return 0; |
| |
886 } |
| |
887 |
| |
888 int cxListDifference(CxList *dst, |
| |
889 const CxList *minuend, const CxList *subtrahend, |
| |
890 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { |
| |
891 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
892 |
| |
893 // optimize for sorted collections |
| |
894 if (cxCollectionSorted(minuend) && cxCollectionSorted(subtrahend)) { |
| |
895 bool dst_was_empty = cxCollectionSize(dst) == 0; |
| |
896 |
| |
897 CxIterator min_iter = cxListIterator(minuend); |
| |
898 CxIterator sub_iter = cxListIterator(subtrahend); |
| |
899 while (cxIteratorValid(min_iter)) { |
| |
900 void *min_elem = cxIteratorCurrent(min_iter); |
| |
901 void *sub_elem; |
| |
902 int d; |
| |
903 if (cxIteratorValid(sub_iter)) { |
| |
904 sub_elem = cxIteratorCurrent(sub_iter); |
| |
905 cx_compare_func cmp = subtrahend->collection.cmpfunc; |
| |
906 d = cmp(sub_elem, min_elem); |
| |
907 } else { |
| |
908 // no more elements in the subtrahend, |
| |
909 // i.e., the min_elem is larger than any elem of the subtrahend |
| |
910 d = 1; |
| |
911 } |
| |
912 if (d == 0) { |
| |
913 // is contained, so skip it |
| |
914 cxIteratorNext(min_iter); |
| |
915 } else if (d < 0) { |
| |
916 // subtrahend is smaller than minuend, |
| |
917 // check the next element |
| |
918 cxIteratorNext(sub_iter); |
| |
919 } else { |
| |
920 // subtrahend is larger than the dst element, |
| |
921 // clone the minuend and advance |
| |
922 void **dst_mem = cxListEmplace(dst); |
| |
923 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
924 void* dst_ptr = clone_func(target, min_elem, clone_allocator, data); |
| |
925 if (dst_ptr == NULL) { |
| |
926 cx_list_pop_uninitialized_elements(dst, 1); |
| |
927 return 1; |
| |
928 } |
| |
929 if (cxCollectionStoresPointers(dst)) { |
| |
930 *dst_mem = dst_ptr; |
| |
931 } |
| |
932 cxIteratorNext(min_iter); |
| |
933 } |
| |
934 } |
| |
935 |
| |
936 // if dst was empty, it is now guaranteed to be sorted |
| |
937 dst->collection.sorted = dst_was_empty; |
| |
938 } else { |
| |
939 CxIterator min_iter = cxListIterator(minuend); |
| |
940 cx_foreach(void *, elem, min_iter) { |
| |
941 if (cxListContains(subtrahend, elem)) { |
| |
942 continue; |
| |
943 } |
| |
944 void **dst_mem = cxListEmplace(dst); |
| |
945 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
946 void* dst_ptr = clone_func(target, elem, clone_allocator, data); |
| |
947 if (dst_ptr == NULL) { |
| |
948 cx_list_pop_uninitialized_elements(dst, 1); |
| |
949 return 1; |
| |
950 } |
| |
951 if (cxCollectionStoresPointers(dst)) { |
| |
952 *dst_mem = dst_ptr; |
| |
953 } |
| |
954 } |
| |
955 } |
| |
956 |
| |
957 return 0; |
| |
958 } |
| |
959 |
| |
960 int cxListIntersection(CxList *dst, |
| |
961 const CxList *src, const CxList *other, |
| |
962 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { |
| |
963 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
964 |
| |
965 // optimize for sorted collections |
| |
966 if (cxCollectionSorted(src) && cxCollectionSorted(other)) { |
| |
967 bool dst_was_empty = cxCollectionSize(dst) == 0; |
| |
968 |
| |
969 CxIterator src_iter = cxListIterator(src); |
| |
970 CxIterator other_iter = cxListIterator(other); |
| |
971 while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) { |
| |
972 void *src_elem = cxIteratorCurrent(src_iter); |
| |
973 void *other_elem = cxIteratorCurrent(other_iter); |
| |
974 int d = src->collection.cmpfunc(src_elem, other_elem); |
| |
975 if (d == 0) { |
| |
976 // is contained, clone it |
| |
977 void **dst_mem = cxListEmplace(dst); |
| |
978 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
979 void* dst_ptr = clone_func(target, src_elem, clone_allocator, data); |
| |
980 if (dst_ptr == NULL) { |
| |
981 cx_list_pop_uninitialized_elements(dst, 1); |
| |
982 return 1; |
| |
983 } |
| |
984 if (cxCollectionStoresPointers(dst)) { |
| |
985 *dst_mem = dst_ptr; |
| |
986 } |
| |
987 cxIteratorNext(src_iter); |
| |
988 } else if (d < 0) { |
| |
989 // the other element is larger, skip the source element |
| |
990 cxIteratorNext(src_iter); |
| |
991 } else { |
| |
992 // the source element is larger, try to find it in the other list |
| |
993 cxIteratorNext(other_iter); |
| |
994 } |
| |
995 } |
| |
996 |
| |
997 // if dst was empty, it is now guaranteed to be sorted |
| |
998 dst->collection.sorted = dst_was_empty; |
| |
999 } else { |
| |
1000 CxIterator src_iter = cxListIterator(src); |
| |
1001 cx_foreach(void *, elem, src_iter) { |
| |
1002 if (!cxListContains(other, elem)) { |
| |
1003 continue; |
| |
1004 } |
| |
1005 void **dst_mem = cxListEmplace(dst); |
| |
1006 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
1007 void* dst_ptr = clone_func(target, elem, clone_allocator, data); |
| |
1008 if (dst_ptr == NULL) { |
| |
1009 cx_list_pop_uninitialized_elements(dst, 1); |
| |
1010 return 1; |
| |
1011 } |
| |
1012 if (cxCollectionStoresPointers(dst)) { |
| |
1013 *dst_mem = dst_ptr; |
| |
1014 } |
| |
1015 } |
| |
1016 } |
| |
1017 |
| |
1018 return 0; |
| |
1019 } |
| |
1020 |
| |
1021 int cxListUnion(CxList *dst, |
| |
1022 const CxList *src, const CxList *other, |
| |
1023 cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { |
| |
1024 if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; |
| |
1025 |
| |
1026 // optimize for sorted collections |
| |
1027 if (cxCollectionSorted(src) && cxCollectionSorted(other)) { |
| |
1028 bool dst_was_empty = cxCollectionSize(dst) == 0; |
| |
1029 |
| |
1030 CxIterator src_iter = cxListIterator(src); |
| |
1031 CxIterator other_iter = cxListIterator(other); |
| |
1032 while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) { |
| |
1033 void *src_elem = NULL, *other_elem = NULL; |
| |
1034 int d; |
| |
1035 if (!cxIteratorValid(src_iter)) { |
| |
1036 other_elem = cxIteratorCurrent(other_iter); |
| |
1037 d = 1; |
| |
1038 } else if (!cxIteratorValid(other_iter)) { |
| |
1039 src_elem = cxIteratorCurrent(src_iter); |
| |
1040 d = -1; |
| |
1041 } else { |
| |
1042 src_elem = cxIteratorCurrent(src_iter); |
| |
1043 other_elem = cxIteratorCurrent(other_iter); |
| |
1044 d = src->collection.cmpfunc(src_elem, other_elem); |
| |
1045 } |
| |
1046 void *clone_from; |
| |
1047 if (d < 0) { |
| |
1048 // source element is smaller clone it |
| |
1049 clone_from = src_elem; |
| |
1050 cxIteratorNext(src_iter); |
| |
1051 } else if (d == 0) { |
| |
1052 // both elements are equal, clone from the source, skip other |
| |
1053 clone_from = src_elem; |
| |
1054 cxIteratorNext(src_iter); |
| |
1055 cxIteratorNext(other_iter); |
| |
1056 } else { |
| |
1057 // the other element is smaller, clone it |
| |
1058 clone_from = other_elem; |
| |
1059 cxIteratorNext(other_iter); |
| |
1060 } |
| |
1061 void **dst_mem = cxListEmplace(dst); |
| |
1062 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
1063 void* dst_ptr = clone_func(target, clone_from, clone_allocator, data); |
| |
1064 if (dst_ptr == NULL) { |
| |
1065 cx_list_pop_uninitialized_elements(dst, 1); |
| |
1066 return 1; |
| |
1067 } |
| |
1068 if (cxCollectionStoresPointers(dst)) { |
| |
1069 *dst_mem = dst_ptr; |
| |
1070 } |
| |
1071 } |
| |
1072 |
| |
1073 // if dst was empty, it is now guaranteed to be sorted |
| |
1074 dst->collection.sorted = dst_was_empty; |
| |
1075 } else { |
| |
1076 if (cxListClone(dst, src, clone_func, clone_allocator, data)) { |
| |
1077 return 1; |
| |
1078 } |
| |
1079 CxIterator other_iter = cxListIterator(other); |
| |
1080 cx_foreach(void *, elem, other_iter) { |
| |
1081 if (cxListContains(src, elem)) { |
| |
1082 continue; |
| |
1083 } |
| |
1084 void **dst_mem = cxListEmplace(dst); |
| |
1085 void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; |
| |
1086 void* dst_ptr = clone_func(target, elem, clone_allocator, data); |
| |
1087 if (dst_ptr == NULL) { |
| |
1088 cx_list_pop_uninitialized_elements(dst, 1); |
| |
1089 return 1; |
| |
1090 } |
| |
1091 if (cxCollectionStoresPointers(dst)) { |
| |
1092 *dst_mem = dst_ptr; |
| |
1093 } |
| |
1094 } |
| |
1095 } |
| |
1096 |
| |
1097 return 0; |
| |
1098 } |
| |
1099 |
| |
1100 int cxListCloneSimple(CxList *dst, const CxList *src) { |
| |
1101 return cxListClone(dst, src, use_simple_clone_func(src)); |
| |
1102 } |
| |
1103 |
| |
1104 int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) { |
| |
1105 return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); |
| |
1106 } |
| |
1107 |
| |
1108 int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) { |
| |
1109 return cxListIntersection(dst, src, other, use_simple_clone_func(src)); |
| |
1110 } |
| |
1111 |
| |
1112 int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) { |
| |
1113 return cxListUnion(dst, src, other, use_simple_clone_func(src)); |
| |
1114 } |
| |
1115 |
| |
1116 int cxListReserve(CxList *list, size_t capacity) { |
| |
1117 if (list->cl->change_capacity == NULL) { |
| |
1118 return 0; |
| |
1119 } |
| |
1120 if (capacity <= cxCollectionSize(list)) { |
| |
1121 return 0; |
| |
1122 } |
| |
1123 return list->cl->change_capacity(list, capacity); |
| |
1124 } |
| |
1125 |
| |
1126 int cxListShrink(CxList *list) { |
| |
1127 if (list->cl->change_capacity == NULL) { |
| |
1128 return 0; |
| |
1129 } |
| |
1130 return list->cl->change_capacity(list, cxCollectionSize(list)); |
| |
1131 } |