From c016591b71d5a5da52542f0abbafc0f14d04ef48 Mon Sep 17 00:00:00 2001 From: Tom Damerose <50496792+tdameros@users.noreply.github.com> Date: Sun, 4 Aug 2024 00:30:47 +0200 Subject: [PATCH] ft_list_sort (#9) * feat: implement create_elem, list_push_front, list_size, list_remove_if * style: format * fix: add valgrind to github action dependencies * feat: implement ft_sort_list using merge sort --- .clang-format | 2 +- Makefile | 5 +- include/libasm.h | 1 + src/ft_list_sort.s | 241 +++++++++++++++++++++++++++++++++++++++ test/ft_list_remove_if.c | 8 +- test/ft_list_sort.c | 113 ++++++++++++++++++ test/utils.c | 36 ++++++ test/utils.h | 24 ++++ 8 files changed, 421 insertions(+), 9 deletions(-) create mode 100644 src/ft_list_sort.s create mode 100644 test/ft_list_sort.c create mode 100644 test/utils.c create mode 100644 test/utils.h diff --git a/.clang-format b/.clang-format index b1392af..b8cbfcf 100644 --- a/.clang-format +++ b/.clang-format @@ -16,7 +16,7 @@ AllowAllParametersOfDeclarationOnNextLine: true AllowShortEnumsOnASingleLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All +AllowShortFunctionsOnASingleLine: None AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortLoopsOnASingleLine: true diff --git a/Makefile b/Makefile index e35f469..8d35450 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ LIST_ASM_SRC = \ ft_list_push_front.s \ ft_list_remove_if.s \ ft_list_size.s \ + ft_list_sort.s \ ft_read.s \ ft_strcmp.s \ ft_strcpy.s \ @@ -23,13 +24,15 @@ LIST_TEST_SRC = \ ft_list_push_front.c \ ft_list_remove_if.c \ ft_list_size.c \ + ft_list_sort.c \ ft_read.c \ ft_strcmp.c \ ft_strcpy.c \ ft_strdup.c \ ft_strlen.c \ ft_write.c \ - main.c + main.c \ + utils.c # ------------ DIRECTORIES ------------ # diff --git a/include/libasm.h b/include/libasm.h index eb4ea1b..2c9c110 100644 --- a/include/libasm.h +++ b/include/libasm.h @@ -33,5 +33,6 @@ t_list *ft_create_elem(void *data); void ft_list_push_front(t_list **begin_list, void *data); int ft_list_size(t_list *begin_list); void ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)(), void (*free_fct)(void *)); +void ft_list_sort(t_list **begin_list, int (*cmp)()); #endif \ No newline at end of file diff --git a/src/ft_list_sort.s b/src/ft_list_sort.s new file mode 100644 index 0000000..5259a3d --- /dev/null +++ b/src/ft_list_sort.s @@ -0,0 +1,241 @@ +bits 64 + +%include "ft_list.s" + +section .note.GNU-stack + +section .text + global ft_list_sort + +;typedef struct s_list +;{ +; void *data; +; struct s_list *next; +;} t_list; + +; void ft_list_sort(t_list **head, int (*cmp)()) { +; if (head == NULL || *head == NULL || (*head)->next == NULL) { +; return; +; } +; +; t_list *middle = get_middle(*head); +; t_list *next_to_middle = middle->next; +; middle->next = NULL; +; +; ft_list_sort(head, cmp); +; return; +; ft_list_sort(&next_to_middle, cmp); +; +; *head = sorted_merge(*head, next_to_middle, cmp); +; } + +; t_list* sorted_merge(t_list *left, t_list *right, int (*cmp)(void*, void*)) { +; if (left == NULL) { +; return right; +; } +; if (right == NULL) { +; return left; +; } +; +; t_list *result = NULL; +; +; if (cmp(left->data, right->data) <= 0) { +; result = left; +; result->next = sorted_merge(left->next, right, cmp); +; } else { +; result = right; +; result->next = sorted_merge(left, right->next, cmp); +; } +; +; return result; +; } + +; t_list* get_middle(t_list *head) { +; if (head == NULL) { +; return head; +; } +; +; t_list *slow = head; +; t_list *fast = head; +; +; while (fast->next != NULL && fast->next->next != NULL) { +; slow = slow->next; +; fast = fast->next->next; +; } +; +; printf("get_middle result: %p\n", slow); +; return slow; +; } + +;void ft_list_sort(t_list **begin_list, int (*cmp)()); +ft_list_sort: + test rdi, rdi + jz ft_list_sort_end + mov r8, [rdi] + test r8, r8 + jz ft_list_sort_end + mov r8, [r8 + LIST_NEXT_OFFSET] + test r8, r8 + jz ft_list_sort_end + sub rsp, 16 + push rdi + push rsi + mov rdi, [rdi] + call get_middle + pop rsi + pop rdi + ; middle + mov [rsp + 0], rax + ; next_to_middle + mov rax, [rax + LIST_NEXT_OFFSET] + mov [rsp + 8], rax + ; middle->next = 0 + mov rax, [rsp + 0] + mov qword [rax + LIST_NEXT_OFFSET], 0 + + + push rdi + push rsi + call ft_list_sort + pop rsi + pop rdi + + mov r8, rsp + add r8, 8 + push rdi + push rsi + mov rdi, r8 + call ft_list_sort + ; right + pop rsi + pop rdi + + mov r8, [rsp + 8] + push rdi + push rsi + mov rdi, [rdi] + mov rdx, rsi + mov rsi, r8 + call sorted_merge + pop rsi + pop rdi + mov [rdi], rax + add rsp, 16 + ret + +ft_list_sort_end: + ret + + +;t_list* sorted_merge(t_list *left, t_list *right, int (*cmp)(void*, void*)) +sorted_merge: + test rdi, rdi + jz sorted_merge_ret_right + test rsi, rsi + jz sorted_merge_ret_left + ; result + sub rsp, 8 + mov qword [rsp + 0], 0 + push rdi + push rsi + push rdx + mov rdi, [rdi + LIST_DATA_OFFSET] + mov rsi, [rsi + LIST_DATA_OFFSET] + call rdx + pop rdx + pop rsi + pop rdi + ; cmp only int32_t + cmp eax, 0 + jle sorted_merge_below_or_equal + jmp sorted_merge_greater + +sorted_merge_ret_left: + mov rax, rdi + ret + +sorted_merge_ret_right: + mov rax, rsi + ret + +sorted_merge_below_or_equal: + mov [rsp + 0], rdi + push rdi + push rsi + push rdx + mov rdi, [rdi + LIST_NEXT_OFFSET] + call sorted_merge + pop rdx + pop rsi + pop rdi + mov r8, [rsp + 0] + mov [r8 + LIST_NEXT_OFFSET], rax + jmp sorted_merge_end + +sorted_merge_greater: + mov [rsp + 0], rsi + push rdi + push rsi + push rdx + mov rsi, [rsi + LIST_NEXT_OFFSET] + call sorted_merge + pop rdx + pop rsi + pop rdi + mov r8, [rsp + 0] + mov [r8 + LIST_NEXT_OFFSET], rax + jmp sorted_merge_end + +sorted_merge_end: + mov rax, [rsp + 0] + add rsp, 8 + ret + +;t_list* get_middle(t_list *head) +get_middle: + test rdi, rdi + jz get_middle_ret_head + jnz get_middle_loop + +get_middle_loop: + sub rsp, 16 + ; slow + mov [rsp + 0], rdi + ; fast + mov [rsp + 8], rdi + jmp get_middle_loop_condition + +get_middle_loop_condition: + mov r8, [rsp + 8] + mov r8, [r8 + LIST_NEXT_OFFSET] + test r8, r8 + jnz get_middle_loop_condition2 + jmp get_middle_ret_slow + +get_middle_loop_condition2: + mov r8, [r8 + LIST_NEXT_OFFSET] + test r8, r8 + jnz get_middle_loop_routine + jmp get_middle_ret_slow + +get_middle_loop_routine: + mov r8, [rsp + 0] + mov r8, [r8 + LIST_NEXT_OFFSET] + mov [rsp + 0], r8 + mov r8, [rsp + 8] + mov r8, [r8 + LIST_NEXT_OFFSET] + mov r8, [r8 + LIST_NEXT_OFFSET] + mov [rsp + 8], r8 + jmp get_middle_loop_condition + +get_middle_ret_head: + mov rax, rdi + ret + +get_middle_ret_slow: + mov rax, [rsp + 0] + add rsp, 16 + ret + +end: + ret diff --git a/test/ft_list_remove_if.c b/test/ft_list_remove_if.c index 73c0452..e8906d4 100644 --- a/test/ft_list_remove_if.c +++ b/test/ft_list_remove_if.c @@ -14,6 +14,7 @@ #include "libasm.h" #include "utest.h" +#include "utils.h" UTEST(ft_list_remove_if, null_list_ptr) { t_list **list_ptr = NULL; @@ -29,13 +30,6 @@ UTEST(ft_list_remove_if, null_list) { ASSERT_EQ(errno, 0); } -static int my_strcmp(const void *s1, const void *s2) { - if (s1 == NULL || s2 == NULL) { - return 1; - } - return strcmp(s1, s2); -} - UTEST(ft_list_remove_if, null_data_ref) { t_list *list = NULL; char *data = strdup("Hello, World!"); diff --git a/test/ft_list_sort.c b/test/ft_list_sort.c new file mode 100644 index 0000000..da37b5f --- /dev/null +++ b/test/ft_list_sort.c @@ -0,0 +1,113 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ft_list_sort.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tdameros +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/07/25 18:35:00 by tdameros #+# #+# */ +/* Updated: 2024/07/25 18:35:00 by tdameros ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include + +#include "libasm.h" +#include "utest.h" +#include "utils.h" + +UTEST(ft_list_sort, null_list) { + ft_list_sort(NULL, &strcmp); + ASSERT_EQ(errno, 0); +} + +UTEST(ft_list_sort, empty_list) { + t_list *list = NULL; + ft_list_sort(&list, &strcmp); + ASSERT_EQ(list, NULL); +} + +UTEST(ft_list_sort, null_cmp) { + t_list *list = ft_create_elem("Hello, World!"); + ft_list_sort(&list, NULL); + ASSERT_EQ(list->data, "Hello, World!"); + ASSERT_EQ(list->next, NULL); + free_list(list); +} + +UTEST(ft_list_sort, single_node) { + t_list *list = NULL; + int a = 42; + + ft_list_push_front(&list, &a); + + ft_list_sort(&list, compare_int); + ASSERT_EQ(*(int *)list->data, 42); + free_list(list); +} + +UTEST(ft_list_sort, two_nodes) { + t_list *list = NULL; + int a = 42; + int b = 1; + + ft_list_push_front(&list, &a); + ft_list_push_front(&list, &b); + + ft_list_sort(&list, compare_int); + ASSERT_EQ(*(int *)list->data, 1); + ASSERT_EQ(*(int *)list->next->data, 42); + free_list(list); +} + +UTEST(ft_list_sort, multiple_nodes) { + t_list *list = NULL; + int a = 42; + int b = 1; + int c = 21; + int d = 3; + int e = 7; + int expected[] = {1, 3, 7, 21, 42}; + + ft_list_push_front(&list, &a); + ft_list_push_front(&list, &b); + ft_list_push_front(&list, &c); + ft_list_push_front(&list, &d); + ft_list_push_front(&list, &e); + + ft_list_sort(&list, &compare_int); + + t_list *tmp = list; + for (int i = 0; i < 5; i++) { + ASSERT_EQ(*(int *)tmp->data, expected[i]); + tmp = tmp->next; + } + + free_list(list); +} + +UTEST(ft_list_sort, mutiple_strings) { + t_list *list = NULL; + char *a = "Hello"; + char *b = "World"; + char *c = "42"; + char *d = "21"; + char *e = "7"; + char *expected[] = {"21", "42", "7", "Hello", "World"}; + + ft_list_push_front(&list, a); + ft_list_push_front(&list, b); + ft_list_push_front(&list, c); + ft_list_push_front(&list, d); + ft_list_push_front(&list, e); + + ft_list_sort(&list, &strcmp); + + t_list *tmp = list; + for (int i = 0; i < 5; i++) { + ASSERT_EQ(strcmp((char *)tmp->data, expected[i]), 0); + tmp = tmp->next; + } + + free_list(list); +} diff --git a/test/utils.c b/test/utils.c new file mode 100644 index 0000000..185482f --- /dev/null +++ b/test/utils.c @@ -0,0 +1,36 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* utils.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tdameros +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/08/04 00:03:00 by tdameros #+# #+# */ +/* Updated: 2024/08/04 00:03:00 by tdameros ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#include +#include + +#include "libasm.h" + +int compare_int(void *a, void *b) { + return (*(int *)a - *(int *)b); +} + +void free_list(t_list *head) { + t_list *tmp; + while (head) { + tmp = head; + head = head->next; + free(tmp); + } +} + +int my_strcmp(const void *s1, const void *s2) { + if (s1 == NULL || s2 == NULL) { + return 1; + } + return strcmp(s1, s2); +} diff --git a/test/utils.h b/test/utils.h new file mode 100644 index 0000000..511ed24 --- /dev/null +++ b/test/utils.h @@ -0,0 +1,24 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* utils.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tdameros +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/08/04 00:03:00 by tdameros #+# #+# */ +/* Updated: 2024/08/04 00:03:00 by tdameros ### ########lyon.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef INC_42_LIBASM_TEST_UTILS_H_ +#define INC_42_LIBASM_TEST_UTILS_H_ + +#include "libasm.h" + +int compare_int(void *a, void *b); + +void free_list(t_list *head); + +int my_strcmp(const void *s1, const void *s2); + +#endif \ No newline at end of file