diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3f4dc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.idea +.vscode +*.o +libasm.so +*.gcda +*.gcno +unit_tests diff --git a/B-ASM-400_minilibc.pdf b/B-ASM-400_minilibc.pdf new file mode 100644 index 0000000..e064e9a Binary files /dev/null and b/B-ASM-400_minilibc.pdf differ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af43cba --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +## +## EPITECH PROJECT, 2023 +## MiniLibC +## File description: +## MAKEFILE file. +## + +# Source files +SRC = src/strlen.asm \ + src/strchr.asm \ + src/strrchr.asm \ + src/memset.asm \ + src/memcpy.asm \ + src/strcmp.asm \ + src/strcasecmp.asm \ + src/strncmp.asm \ + src/strpbrk.asm + +# Object files +OBJ = $(SRC:.asm=.o) + +# Output library name +NAME = libasm.so + +# Output tests name +TESTS_NAME = unit_tests + +# Method to compile the .asm files +$(OBJ): + for asm in $(SRC); do \ + nasm -f elf64 $$asm; \ + done + +# Method to compile the library +$(NAME): $(OBJ) + ld -shared -o $(NAME) $(OBJ) + +# Main method +all: $(NAME) + +# Method to remove the .o files +clean: + rm -f $(OBJ) + +# Method to remove the library +fclean: clean + rm -f $(NAME) + +# Method to recompile the all project +re: fclean all + +# Method to test the library +tests_run: re + gcc -lcriterion --coverage -o $(TESTS_NAME) $(OBJ) tests/*.c + LD_PRELOAD=./$(NAME) ./unit_tests + +# To avoid relinking +.PHONY: all clean fclean re diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9c528d --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# MiniLibC + +[![Continuous Integration](https://github.com/matheograil/mini-lib-c/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/matheograil/mini-lib-c/actions/workflows/ci.yml) + +Here are the functions to be implemented in your MiniLibC: +- strlen +- strchr +- strrchr +- memset +- memcpy +- strcmp +- memmove +- strncmp +- strcasecmp +- strstr +- strpbrk +- strcspn diff --git a/src/memcpy.asm b/src/memcpy.asm new file mode 100644 index 0000000..79d9bef --- /dev/null +++ b/src/memcpy.asm @@ -0,0 +1,29 @@ +BITS 64 ; 64-bits mode + +section .text + global memcpy ; The "memcpy" function must be callable outside + +memcpy: + ; "rdi" corresponds to the destination string + ; "rsi" corresponds to the source string + ; "rdx" corresponds to the number of characters to copy + + mov rax, rdi ; The return value is the destination string + + call memcpy_loop + +memcpy_loop: + cmp rdx, 0 + je memcpy_end ; If the index is 0, we stop + + mov bh, [rsi] ; We not use "al" because "rax" is the return value, and "bl" becuase it's proctected + mov [rdi], bh ; Replaces the current character (only first byte) + + dec rdx ; Decrements the index + inc rsi ; Moves to the next character (source) + inc rdi ; Moves to the next character (destination) + + jmp memcpy_loop ; Recursivity + +memcpy_end: + ret diff --git a/src/memset.asm b/src/memset.asm new file mode 100644 index 0000000..a8dae83 --- /dev/null +++ b/src/memset.asm @@ -0,0 +1,27 @@ +BITS 64 ; 64-bits mode + +section .text + global memset ; The "memset" function must be callable outside + +memset: + ; "rdi" corresponds to the string + ; "rsi" corresponds to the character to set + ; "rdx" corresponds to the number of characters to set + + mov rax, rdi ; The return value is the same string as the parameter + + call memset_loop + +memset_loop: + cmp rdx, 0 + je memset_end ; If the index is 0, we stop + + mov [rdi], sil ; Replaces the current character (only first byte) + + inc rdi ; Moves to the next character + dec rdx ; Decrements the index + + jmp memset_loop ; Recursivity + +memset_end: + ret \ No newline at end of file diff --git a/src/strcasecmp.asm b/src/strcasecmp.asm new file mode 100644 index 0000000..2fa524d --- /dev/null +++ b/src/strcasecmp.asm @@ -0,0 +1,56 @@ +BITS 64 ; 64-bits mode + +section .text + global strcasecmp ; The "strcasecmp" function must be callable outside + +strcasecmp: + ; "rdi" corresponds to the first string + ; "rsi" corresponds to the second string + + mov al, [rdi] ; Stores the current character (first string) + mov ah, [rsi] ; Stores the current character (second string) + + cmp al, 0 ; Checks if the first string is finished + je strcasecmp_end_first + cmp ah, 0 ; Checks if the second string is finished + je strcasecmp_end_second + + or al, 32 ; Converts the current character to lowercase (first string) + or ah, 32 ; Converts the current character to lowercase (second string) + + cmp al, ah ; Compares the current characters + je strcasecmp_equal + ja strcasecmp_greater + jb strcasecmp_lower + +strcasecmp_equal: + inc rdi ; Moves to the next character (first string) + inc rsi ; Moves to the next character (second string) + + jmp strcasecmp ; Recursivity + +strcasecmp_greater: + mov rax, 1 + ret + +strcasecmp_lower: + mov rax, -1 + ret + +strcasecmp_end_first: + cmp ah, 0 ; Checks if the both strings are finished + je strcasecmp_end_both + + mov rax, -1 + ret + +strcasecmp_end_second: + cmp al, 0 ; Checks if the both strings are finished + je strcasecmp_end_both + + mov rax, 1 + ret + +strcasecmp_end_both: + mov rax, 0 + ret diff --git a/src/strchr.asm b/src/strchr.asm new file mode 100644 index 0000000..73f9167 --- /dev/null +++ b/src/strchr.asm @@ -0,0 +1,23 @@ +BITS 64 ; 64-bits mode + +section .text + global strchr ; The "strchr" function must be callable outside + +strchr: + cmp [rdi], sil + je strchr_found ; If the current character corresponds to the one we are looking for, returns a pointer to it + + cmp [rdi], byte 0 + je strchr_not_found ; If the current character is null, returns a null pointer + + inc rdi ; Moves to the next character + + jmp strchr ; Recursivity + +strchr_found: + mov rax, rdi + ret + +strchr_not_found: + mov rax, 0 + ret diff --git a/src/strcmp.asm b/src/strcmp.asm new file mode 100644 index 0000000..b291550 --- /dev/null +++ b/src/strcmp.asm @@ -0,0 +1,53 @@ +BITS 64 ; 64-bits mode + +section .text + global strcmp ; The "strcmp" function must be callable outside + +strcmp: + ; "rdi" corresponds to the first string + ; "rsi" corresponds to the second string + + mov al, [rdi] ; Stores the current character (first string) + mov ah, [rsi] ; Stores the current character (second string) + + cmp al, 0 ; Checks if the first string is finished + je strcmp_end_first + cmp ah, 0 ; Checks if the second string is finished + je strcmp_end_second + + cmp al, ah ; Compares the current characters + je strcmp_equal + ja strcmp_greater + jb strcmp_lower + +strcmp_equal: + inc rdi ; Moves to the next character (first string) + inc rsi ; Moves to the next character (second string) + + jmp strcmp ; Recursivity + +strcmp_greater: + mov rax, 1 + ret + +strcmp_lower: + mov rax, -1 + ret + +strcmp_end_first: + cmp ah, 0 ; Checks if the both strings are finished + je strcmp_end_both + + mov rax, -1 + ret + +strcmp_end_second: + cmp al, 0 ; Checks if the both strings are finished + je strcmp_end_both + + mov rax, 1 + ret + +strcmp_end_both: + mov rax, 0 + ret diff --git a/src/strlen.asm b/src/strlen.asm new file mode 100644 index 0000000..a18fe92 --- /dev/null +++ b/src/strlen.asm @@ -0,0 +1,21 @@ +BITS 64 ; 64-bits mode + +section .text + global strlen ; The "strlen" function must be callable outside + +strlen: + mov rax, 0 ; Initializes the counter to 0 + + call strlen_loop ; Calls the "strlen_loop" function + +strlen_loop: + cmp [rdi], byte 0 ; Is the current character equal to 0 ? + je strlen_loop_end ; If yes, we're done + + inc rax ; Increments the counter + inc rdi ; Moves to the next character + + jmp strlen_loop ; Recursivity + +strlen_loop_end: + ret diff --git a/src/strncmp.asm b/src/strncmp.asm new file mode 100644 index 0000000..4d2a9e6 --- /dev/null +++ b/src/strncmp.asm @@ -0,0 +1,58 @@ +BITS 64 ; 64-bits mode + +section .text + global strncmp ; The "strncmp" function must be callable outside + +strncmp: + ; "rdi" corresponds to the first string + ; "rsi" corresponds to the second string + ; "rdx" corresponds to the number of characters to compare + + mov al, [rdi] ; Stores the current character (first string) + mov ah, [rsi] ; Stores the current character (second string) + + cmp al, 0 ; Checks if the first string is finished + je strncmp_end_first + cmp ah, 0 ; Checks if the second string is finished + je strncmp_end_second + + cmp rdx, 1 + je strncmp_end_both + + cmp al, ah ; Compares the current characters + je strncmp_equal + ja strncmp_greater + jb strncmp_lower + +strncmp_equal: + inc rdi ; Moves to the next character (first string) + inc rsi ; Moves to the next character (second string) + dec rdx ; Decreases the number of characters to compare + + jmp strncmp ; Recursivity + +strncmp_greater: + mov rax, 1 + ret + +strncmp_lower: + mov rax, -1 + ret + +strncmp_end_first: + cmp ah, 0 ; Checks if the both strings are finished + je strncmp_end_both + + mov rax, -1 + ret + +strncmp_end_second: + cmp al, 0 ; Checks if the both strings are finished + je strncmp_end_both + + mov rax, 1 + ret + +strncmp_end_both: + mov rax, 0 + ret diff --git a/src/strpbrk.asm b/src/strpbrk.asm new file mode 100644 index 0000000..e3e168a --- /dev/null +++ b/src/strpbrk.asm @@ -0,0 +1,53 @@ +BITS 64 ; 64-bits mode + +section .text + global strpbrk ; The "strpbrk" function must be callable outside + +strpbrk: + ; "rdi" corresponds to the string + ; "rsi" corresponds to the query + + cmp [rdi], byte 0 + je strpbrk_end ; At the end of the string, returns NULL + + mov rdx, rsi ; Saves the query address + + call strpbrk_search_loop + + mov rsi, rdx ; Restores the query address + + cmp bl, 1 + je strpbrk_found ; If the character is found, returns a pointer to current character (string) + + inc rdi ; Moves to the next character (string) + + jmp strpbrk ; Recursivity + +strpbrk_found: + mov rax, rdi + ret + +strpbrk_end: + mov rax, 0 + ret + +strpbrk_search_loop: + cmp [rsi], byte 0 + je strpbrk_search_end ; At the end of the query, returns "0" + + mov al, [rdi] + mov ah, [rsi] + cmp al, ah + je strpbrk_search_found ; If the character is found, returns "1" + + inc rsi ; Moves to the next character (query) + + jmp strpbrk_search_loop ; Recursivity + +strpbrk_search_end: + mov bl, 0 + ret + +strpbrk_search_found: + mov bl, 1 + ret \ No newline at end of file diff --git a/src/strrchr.asm b/src/strrchr.asm new file mode 100644 index 0000000..40c285e --- /dev/null +++ b/src/strrchr.asm @@ -0,0 +1,43 @@ +BITS 64 ; 64-bits mode + +section .text + global strrchr ; The "strrchr" function must be callable outside + +strrchr: + mov rbx, -1 ; Initializes the search index to -1 (nothing has been found yet) + mov rdx, 0 ; Initializes the index to 0 + mov rcx, rdi ; Saves the string into another register to conserve the original one + + call strrchr_loop + +strrchr_loop: + cmp [rcx], byte 0 + je strrchr_compute ; If the current character is null, we continue to the computation + + cmp [rcx], sil + je strrchr_loop_save_index ; If the current character corresponds to the one we are looking for, we save the index + + inc rcx ; Moves to the next character + inc rdx ; Increments the index + + jmp strrchr_loop ; Recursivity + +strrchr_loop_save_index: + mov rbx, rdx + + inc rcx + inc rdx + + jmp strrchr_loop ; After saving the index, we continue to the next character + +strrchr_compute: + cmp rbx, -1 + je strrchr_not_found ; If the index is still -1, it means that the character has not been found + + mov rax, rdi + add rax, rbx ; Otherwise, we return the character at the right index + ret + +strrchr_not_found: + mov rax, 0 + ret diff --git a/tests/test_memcpy.c b/tests/test_memcpy.c new file mode 100644 index 0000000..acd2468 --- /dev/null +++ b/tests/test_memcpy.c @@ -0,0 +1,30 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_memcpy.c file. +*/ + +#include +#include +#include + +Test(memcpy, test_with_basic_string) +{ + char *str1 = "Hello World !"; + char *str2 = malloc(sizeof(char) * (strlen(str1) + 1)); + + memcpy(str2, str1, strlen(str1) + 1); + cr_assert_str_eq(str1, str2); + free(str2); +} + +Test(memcpy, test_with_empty_string) +{ + char *str1 = ""; + char *str2 = malloc(sizeof(char) * (strlen(str1) + 1)); + + memcpy(str2, str1, strlen(str1) + 1); + cr_assert_str_eq(str1, str2); + free(str2); +} diff --git a/tests/test_memset.c b/tests/test_memset.c new file mode 100644 index 0000000..b9a78ad --- /dev/null +++ b/tests/test_memset.c @@ -0,0 +1,33 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_memset.c file. +*/ + +#include +#include + +Test(memset, test_with_basic_string) +{ + char str[20] = "Hello World !"; + + memset(str + 6, 'U', 5); + cr_assert_str_eq(str, "Hello UUUUU !"); +} + +Test(memset, test_with_empty_string) +{ + char str[20] = ""; + + memset(str, 'U', 5); + cr_assert_str_eq(str, "UUUUU"); +} + +Test(memset, test_with_basic_string_and_greater_position) +{ + char str[20] = "Hello World !"; + + memset(str + 13, 'U', 5); + cr_assert_str_eq(str, "Hello World !UUUUU"); +} \ No newline at end of file diff --git a/tests/test_strcasecmp.c b/tests/test_strcasecmp.c new file mode 100644 index 0000000..3f410e6 --- /dev/null +++ b/tests/test_strcasecmp.c @@ -0,0 +1,41 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strcasecmp.c file. +*/ + +#include +#include + +Test(strcasecmp, test_with_equal_strings) +{ + char *str1 = "hello world !"; + char *str2 = "Hello World !"; + + cr_assert(strcasecmp(str1, str2) == 0); +} + +Test(strcasecmp, test_with_empty_strings) +{ + char *str1 = ""; + char *str2 = ""; + + cr_assert(strcasecmp(str1, str2) == 0); +} + +Test(strcasecmp, test_with_greater_strings) +{ + char *str1 = "Hello World !"; + char *str2 = "Hello World"; + + cr_assert(strcasecmp(str1, str2) > 0); +} + +Test(strcasecmp, test_with_lower_strings) +{ + char *str1 = "Hello World"; + char *str2 = "Hello World !"; + + cr_assert(strcasecmp(str1, str2) < 0); +} \ No newline at end of file diff --git a/tests/test_strchr.c b/tests/test_strchr.c new file mode 100644 index 0000000..c0d5bab --- /dev/null +++ b/tests/test_strchr.c @@ -0,0 +1,41 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strchr.c file. +*/ + +#include +#include + +Test(strchr, test_with_filled_string_and_match_string) +{ + char *str = "Hello World !"; + char query = 'o'; + + cr_assert_str_eq(strchr(str, query), "o World !"); +} + +Test(strchr, test_with_filled_string_and_match_backslash_zero) +{ + char *str = "Hello World !"; + char query = '\0'; + + cr_assert_str_eq(strchr(str, query), ""); +} + +Test(strchr, test_with_filled_string_and_no_match) +{ + char *str = "Hello World !"; + char query = 'b'; + + cr_assert_eq(strchr(str, query), NULL); +} + +Test(strchr, test_with_empty_string_and_no_match) +{ + char *str = ""; + char query = 'o'; + + cr_assert_eq(strchr(str, query), NULL); +} diff --git a/tests/test_strcmp.c b/tests/test_strcmp.c new file mode 100644 index 0000000..307d53f --- /dev/null +++ b/tests/test_strcmp.c @@ -0,0 +1,41 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strcmp.c file. +*/ + +#include +#include + +Test(strcmp, test_with_equal_strings) +{ + char *str1 = "Hello World !"; + char *str2 = "Hello World !"; + + cr_assert(strcmp(str1, str2) == 0); +} + +Test(strcmp, test_with_empty_strings) +{ + char *str1 = ""; + char *str2 = ""; + + cr_assert(strcmp(str1, str2) == 0); +} + +Test(strcmp, test_with_greater_strings) +{ + char *str1 = "Hello World !"; + char *str2 = "Hello World"; + + cr_assert(strcmp(str1, str2) > 0); +} + +Test(strcmp, test_with_lower_strings) +{ + char *str1 = "Hello World"; + char *str2 = "Hello World !"; + + cr_assert(strcmp(str1, str2) < 0); +} \ No newline at end of file diff --git a/tests/test_strcspn.c b/tests/test_strcspn.c new file mode 100644 index 0000000..779141d --- /dev/null +++ b/tests/test_strcspn.c @@ -0,0 +1,41 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strcspn.c file. +*/ + +#include +#include + +Test(strcspn, test_with_filled_string_with_filled_query_and_match) +{ + char *str = "Hello world !"; + char *query = "wd"; + + cr_assert_eq(strcspn(str, query), 6); +} + +Test(strcspn, test_with_filled_string_with_filled_query_and_no_match) +{ + char *str = "Hello world !"; + char *query = "yz"; + + cr_assert_eq(strcspn(str, query), 13); +} + +Test(strcspn, test_with_empty_string_with_filled_query_and_no_match) +{ + char *str = ""; + char *query = "wd"; + + cr_assert_eq(strcspn(str, query), 0); +} + +Test(strcspn, test_with_filled_string_with_empty_query_and_no_match) +{ + char *str = "Hello world !"; + char *query = ""; + + cr_assert_eq(strcspn(str, query), 13); +} \ No newline at end of file diff --git a/tests/test_strlen.c b/tests/test_strlen.c new file mode 100644 index 0000000..9a95a44 --- /dev/null +++ b/tests/test_strlen.c @@ -0,0 +1,23 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strlen.c file. +*/ + +#include +#include + +Test(strlen, test_with_basic_string) +{ + char *str = "Hello World !"; + + cr_assert_eq(strlen(str), 13); +} + +Test(strlen, test_with_empty_string) +{ + char *str = ""; + + cr_assert_eq(strlen(str), 0); +} \ No newline at end of file diff --git a/tests/test_strncmp.c b/tests/test_strncmp.c new file mode 100644 index 0000000..6e86156 --- /dev/null +++ b/tests/test_strncmp.c @@ -0,0 +1,41 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strncmp.c file. +*/ + +#include +#include + +Test(strncmp, test_with_equal_strings) +{ + char *str1 = "Hello World !"; + char *str2 = "Hello World"; + + cr_assert(strncmp(str1, str2, 11) == 0); +} + +Test(strncmp, test_with_empty_strings) +{ + char *str1 = ""; + char *str2 = ""; + + cr_assert(strncmp(str1, str2, 0) == 0); +} + +Test(strncmp, test_with_greater_strings) +{ + char *str1 = "Hello World !"; + char *str2 = "Hello World"; + + cr_assert(strncmp(str1, str2, 13) > 0); +} + +Test(strncmp, test_with_lower_strings) +{ + char *str1 = "Hello World"; + char *str2 = "Hello World !"; + + cr_assert(strncmp(str1, str2, 13) < 0); +} \ No newline at end of file diff --git a/tests/test_strpbrk.c b/tests/test_strpbrk.c new file mode 100644 index 0000000..54e6a6f --- /dev/null +++ b/tests/test_strpbrk.c @@ -0,0 +1,41 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strpbrk.c file. +*/ + +#include +#include + +Test(strpbrk, test_with_filled_string_with_filled_query_and_match) +{ + char *str = "Hello world !"; + char *query = "wd"; + + cr_assert_str_eq(strpbrk(str, query), "world !"); +} + +Test(strpbrk, test_with_filled_string_with_filled_query_and_no_match) +{ + char *str = "Hello world !"; + char *query = "bz"; + + cr_assert_eq(strpbrk(str, query), NULL); +} + +Test(strpbrk, test_with_empty_string_with_filled_query_and_no_match) +{ + char *str = ""; + char *query = "wd"; + + cr_assert_eq(strpbrk(str, query), NULL); +} + +Test(strpbrk, test_with_filled_string_with_empty_query_and_no_match) +{ + char *str = "Hello world !"; + char *query = ""; + + cr_assert_eq(strpbrk(str, query), NULL); +} \ No newline at end of file diff --git a/tests/test_strrchr.c b/tests/test_strrchr.c new file mode 100644 index 0000000..1e06b83 --- /dev/null +++ b/tests/test_strrchr.c @@ -0,0 +1,33 @@ +/* +** EPITECH PROJECT, 2023 +** MiniLibC +** File description: +** test_strrchr.c file. +*/ + +#include +#include + +Test(strrchr, test_with_filled_string_and_match_string) +{ + char *str = "Hello World !"; + char query = 'o'; + + cr_assert_str_eq(strrchr(str, query), "orld !"); +} + +Test(strrchr, test_with_filled_string_and_no_match) +{ + char *str = "Hello World !"; + char query = 'b'; + + cr_assert_eq(strrchr(str, query), NULL); +} + +Test(strrchr, test_with_empty_string_and_no_match) +{ + char *str = ""; + char query = 'o'; + + cr_assert_eq(strrchr(str, query), NULL); +}