When coding in C at 42, we are limited to using only basic functions and system calls. Typically allowed standard library functions include:
mallocfreeopenclosereadwrite
The very first project of the curriculum, libft, is about building your own library of utility functions. Back then, I was only trying to pass the project without thinking much about optimization — my printf implementation, for example, wrote output byte by byte without any buffering. I was also painfully unaware of data structures and their associated algorithms.
This repository is an improved version of my original libft. It includes optimized implementations of earlier functions (e.g., ft_memset, ft_strlen), and new ones I decided to build due to my interest in systems programming and efficient code. The idea for this "libft on steroids" started when I wrote my own implementation of the SHA256 cryptographic hash function. Comparing the speed of my implementation to openssl was humbling, and it made me very interested in understanding where the huge performance gap was coming from.
Running make in the root of this repository will create a static library libft.a, which you can compile together with other C files.
Note: This README only includes basic documentation, refer to the header files for full usage advice.
// Allocates a buffer of size `bytes_new` and copies `old_buf` into it.// Returns NULL on malloc failure, or if `bytes_old` is larger than `bytes_new`.void*ft_realloc(void*old_buf, uint64_tbytes_new, uint64_tbytes_old) __attribute__((warn_unused_result));
// Allocates a buffer of size `nmemb * size`, 0-initialized bytes.// Returns NULL on malloc failure, or if `nmemb * size` would result in a `uint64_t` overflow.void*ft_calloc(uint64_tnmemb, uint64_tsize) __attribute__((warn_unused_result));
// Returns a heap-allocated duplicate of `src`.// Returns NULL on malloc failure.void*ft_memdup(constvoid*src, constuint64_tbytes) __attribute__((warn_unused_result));inc/mem.h
// Returns a pointer to the first occurence of `needle` in the first `n_bytes` of `haystack`.// If nothing is found, returns a NULL pointer.void*ft_memsrch(constvoid*haystack, intneedle, uint64_tn_bytes);
// Compares the first `n_bytes` bytes `a` to `b`. Returns `0` if they are equal,// or a non-zero u8 if not.shortft_memcmp(constvoid*a, constvoid*b, uint64_tn_bytes);
// Copies the first `n_bytes` of `src` into `dest`.void*ft_memcpy(void*dest, constvoid*src, uint64_tn_bytes);
// Sets the first `n_bytes` of `src` to `c`.void*ft_memset(void*src, intc, uint64_tn_bytes);// Returns the number of bytes between `str` and the next `\0` character.uint64_tft_strlen(constchar*str);
// Returns a heap-allocated duplicate of `str[start..start + len]`.char*ft_substr(charconst*str, unsigned intstart, uint64_tlen);
// Allocates and returns a new string with characters specified in `set` removed// from the beginning and end of the input string `str`char*ft_strtrim(charconst*str, charconst*set);
// Allocates and returns a string built by concatenating `a` and `b`.char*ft_strjoin(char*a, char*b);
// Allocates and returns an array of strings built by splitting `str` by `delimiter`.char**ft_split(charconst*str, chardelimiter);
// Returns a pointer to the first occurrence of `needle` in the first `n_bytes` of// `haystack`. Returns a NULL pointer if nothing is found.char*ft_strnstr(constchar*haystack, constchar*needle, uint64_tn_bytes);
// Converts all characters in `str` to lowercase.char*ft_tolower(char*str);
// Converts all characters in `str` to uppercase.char*ft_toupper(char*str);
// Converts a byte array `nptr` of base `base` into a `bits` bits `is_signed` integer, and stores the// result into `out_value`.boolft_atob(constchar*nptr, intbase, intbits, void*out_value, boolis_signed);
// Converts a numeric value stored in `value` into a string representation in the specified `base`,// and stores the result into the `buffer`.boolft_btoa(void*value, intbase, intbits, char*buffer, uint64_tbuffer_size, boolis_signed, booluppercase);ssize_tft_printf(intfd, constchar*s, ...);AES-256-GCM
// Performs AES-256-GCM authenticated encryption.// // Encrypts the plaintext in `P->msg.data` and generates an authentication// tag. The resulting ciphertext and tag are stored in `C`.voidGCM_AE(Aes256Gcm*constP, Aes256Gcm*constC);
// Performs AES-256-GCM authenticated decryption.// // Decrypts the ciphertext in `C->msg.data` and verifies its authenticity using// the provided authentication tag (`C->tag`). The decrypted plaintext is stored// in `P->msg.data` (provided the authentication step passed).// // The return value marks the status of the authentication step (0 for success,// non-zero for failure).intGCM_AD(Aes256Gcm*constC, Aes256Gcm*constP);
// Sets up the AES-256 context by expanding the provided key and storing the message.voidAES256_Init(Aes256Data*data, constuint8_tkey[AES256_KEY_SIZE_BYTES], constuint8_t*constmsg, constsize_tmsg_len);SHA-256 (streaming)
// Sets up the context with SHA-256's initial hash values and clears all buffers.// This must be called beore any other streaming operations.voidsha256_stream_init(Sha256StreamingContext*constctx);
// Process a chunk of data through the SHA-256 streaming context.//// This function can be called multiple times to incrementally hash large amounts of data. It// handles block boundaries and buffering of incomplete blocks. For optimal performance on very// large inputs, use a buffer of 64KB.voidsha256_stream_update(Sha256StreamingContext*constctx, constuint8_t*data, size_tlen);
// Applies SHA-256 padding, processes any remaining buffered data, and outputs the final hash// as a NULL-terminated hex string.voidsha256_stream_final(Sha256StreamingContext*constctx, char*out);- HashMap: Hash map implementation. Collision resolution can be picked between Binary Search Tree and Linear Probing at compile time.