[{"content":"Tooling for Vapour (External link), a platform for web games.\nvapour-cli (External link) (Go, MIT). Logs in, creates a game project, and builds and publishes web exports for Godot, Unity and Bevy. It scans and signs build zips, does chunked and delta uploads, and runs games locally with an FPS overlay (vapour test). vapour-profiler (External link) (Rust, eBPF). A standalone, local-first eBPF profiler for Wasm runtimes and Linux processes. It uses CO-RE BPF attach/detach, and capture is explicit and bounded. It ships a local performance dashboard and per-asset load observability. ","permalink":"https://madhavkhoslaa.github.io/projects/vapour/","summary":"Go CLI to build and publish web games, and a local-first eBPF profiler.","title":"Vapour CLI and Profiler","topics":[],"type":"project"},{"content":"A Linux network ACL/firewall. eBPF programs run on XDP ingress and TC egress, and a Rust control plane (libbpf-rs) drives them. A ratatui terminal UI edits rules live.\nMAC, IPv4/CIDR and TCP/UDP port filters are chained with BPF prog-array tail calls. Each rule category toggles at runtime, with zero overhead when disabled. The userspace–kernel map-sync layer uses LPM tries and array/hash maps, with a JSON-schema-validated config.\nSource: github.com/madhavkhoslaa/kukri (External link)\n","permalink":"https://madhavkhoslaa.github.io/projects/kukri/","summary":"A Linux network ACL/firewall in Rust and eBPF.","title":"Kukri","topics":[],"type":"project"},{"content":"Mutex, What is it? Example 1 Imagine two people updating the same number on a whiteboard. The number is 0 and both have to increment it by 1.\nThe answer should be 2. But what if both read 0 before either writes anything? Both calculate 1. Both write 1.\nSo the important thing is not just how we increment the number. It is who gets to update it right now.\nA mutex gives one thread ownership of a critical section. Everyone else has to wait. In C, accessing a shared ordinary counter this way without synchronization is a data race, which is undefined behavior. The whiteboard example explains the problem; it is not a prediction of what a racy C program will do.\nWe are going to write a spin mutex. Waiting threads keep checking the lock instead of going to sleep. No futex. Just C atomics and an x86 instruction.\nExample 2 | pthread_create updates a value alone Before we lock anything, here is the smallest pthread program: one worker thread updates an ordinary uint64_t. No mutex yet.\n#include \u0026lt;inttypes.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; #include \u0026lt;stdint.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; static void *add_one(void *arg) { uint64_t *value = arg; /* The worker updates the caller\u0026#39;s value. */ *value += 1; return NULL; } int main(void) { uint64_t value = 0; pthread_t thread; if (pthread_create(\u0026amp;thread, NULL, add_one, \u0026amp;value) != 0) { perror(\u0026#34;pthread_create\u0026#34;); return 1; } pthread_join(thread, NULL); /* Wait for the worker to finish. */ printf(\u0026#34;value: %\u0026#34; PRIu64 \u0026#34;\\n\u0026#34;, value); return 0; }pthread_create takes four arguments: the pthread_t to fill in, attributes (NULL means defaults), the function to run in the new thread, and the single argument passed to that function. Here the argument is \u0026amp;value, so add_one updates the same uint64_t that main owns.\npthread_join blocks until the worker thread has finished. After it returns, main can safely read value.\nOnly one thread ever touches value, so this is not a data race and no lock is needed. The race appears when several threads update the same value at once, which is exactly the whiteboard example above. The mutex we are about to write will protect those concurrent updates.\nHow do we represent ownership? | Let\u0026rsquo;s talk Code. This.\n#include \u0026lt;stdbool.h\u0026gt; #include \u0026lt;stdatomic.h\u0026gt; #include \u0026lt;immintrin.h\u0026gt; _Static_assert(ATOMIC_INT_LOCK_FREE == 2, \u0026#34;Requires lock-free unsigned int atomics\u0026#34;); typedef struct { atomic_uint state; } spin_mutex; #define SPIN_MUTEX_INITIALIZER { .state = 0u }Two states:\n0: nobody owns the mutex. 1: somebody owns the mutex. The static assertion checks that these integer atomics are always lock-free on our target. That does not make our mutex a lock-free algorithm. A thread can still wait indefinitely for the owner!\nInitialize the mutex before sharing it between threads.\nWhy not check for 0 and then write 1? Because two threads could both see 0 before either writes 1. We have recreated the whiteboard problem, except now the number is our lock.\nWe need one atomic operation that writes 1 and tells us what was there before. That is what atomic_exchange_explicit does.\nvoid spin_mutex_lock(spin_mutex *m) { while (atomic_exchange_explicit(\u0026amp;m-\u0026gt;state, 1u, memory_order_acquire)) { while (atomic_load_explicit(\u0026amp;m-\u0026gt;state, memory_order_relaxed)) _mm_pause(); } }If the exchange returns 0, we acquired the mutex. The outer loop ends.\nIf it returns 1, somebody already owns it. We enter the inner loop and wait.\nAn obvious question here is: why do we need two loops?\nThe inner loop Repeatedly exchanging would keep requesting exclusive ownership of the cache line containing state. Imagine everyone repeatedly grabbing the same sign to check whether it says occupied.\nInstead, the inner loop just reads. Waiting cores can share the cache line until an update invalidates their copies. This pattern is called test-and-test-and-set.\nWhen a load sees 0, we try the exchange again. Seeing 0 is not ownership. Somebody else might get there first.\nAnd _mm_pause()? It emits x86 PAUSE, a hint that we are in a spin loop. It can reduce spin-loop penalties and pressure on a sibling hardware thread. It does not put our thread to sleep, and it is not a memory fence.\nWhat are acquire and release doing here? Let\u0026rsquo;s first look at unlocking.\nvoid spin_mutex_unlock(spin_mutex *m) { atomic_store_explicit(\u0026amp;m-\u0026gt;state, 0u, memory_order_release); }Setting the state to 0 makes the mutex available. But ownership is only half the problem. The next owner also needs to see the previous owner\u0026rsquo;s work.\nThread A: acquire =\u0026gt; update protected data =\u0026gt; release | Thread B: acquire reads that 0 =\u0026gt; read protected dataThe release store and the successful acquire exchange establish a happens-before relationship. Thread A\u0026rsquo;s protected writes become visible to Thread B after it acquires the mutex.\nThe inner polling loop uses memory_order_relaxed because it only decides when to retry. We do not access protected data until the exchange succeeds.\nSo yes, the data inside the critical section can be ordinary, non-atomic data. Every concurrent access to it must follow the same locking discipline. volatile is not a replacement for this.\nWhat if I do not want to wait? Use trylock.\nbool spin_mutex_trylock(spin_mutex *m) { unsigned expected = 0u; return atomic_compare_exchange_strong_explicit( \u0026amp;m-\u0026gt;state, \u0026amp;expected, 1u, memory_order_acquire, memory_order_relaxed); }Compare the state with 0. If it matches, atomically replace it with 1 and return true. Otherwise return false. No waiting loop.\nUsing our mutex | Let\u0026rsquo;s create 16 threads. Let\u0026rsquo;s go back to the whiteboard example. This time we have 16 threads, each adding 5 to the same total 100,000 times.\nWhat should the final value be?\n16 threads x 100,000 additions x 5 = 8,000,000The mutex implementation is in mutex.c (External link). Put the following example in main.c (External link), next to it. We include mutex.c directly so this small example needs no separate header. Compile main.c alone: the include already brings in the mutex implementation.\n#include \u0026lt;inttypes.h\u0026gt; #include \u0026lt;pthread.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026lt;string.h\u0026gt; /* Include the implementation once; compile main.c on its own. */ #include \u0026#34;mutex.c\u0026#34; enum { THREAD_COUNT = 16, ADDITIONS_PER_THREAD = 100000, VALUE_PER_ADDITION = 5 }; static spin_mutex mutex = SPIN_MUTEX_INITIALIZER; static atomic_bool start = false; static uint64_t total = 0; /* Ordinary data, protected by the mutex. */ static void *add_value(void *arg) { (void)arg; /* Wait until main has created all worker threads. */ while (!atomic_load_explicit(\u0026amp;start, memory_order_acquire)) _mm_pause(); for (unsigned i = 0; i \u0026lt; ADDITIONS_PER_THREAD; ++i) { spin_mutex_lock(\u0026amp;mutex); total += VALUE_PER_ADDITION; /* The addition is inside the mutex. */ spin_mutex_unlock(\u0026amp;mutex); } return NULL; } static void check_pthread(int error) { if (error != 0) { fprintf(stderr, \u0026#34;pthread: %s\\n\u0026#34;, strerror(error)); exit(EXIT_FAILURE); } } int main(void) { pthread_t threads[THREAD_COUNT]; for (unsigned i = 0; i \u0026lt; THREAD_COUNT; ++i) check_pthread(pthread_create(\u0026amp;threads[i], NULL, add_value, NULL)); atomic_store_explicit(\u0026amp;start, true, memory_order_release); for (unsigned i = 0; i \u0026lt; THREAD_COUNT; ++i) check_pthread(pthread_join(threads[i], NULL)); const uint64_t expected = (uint64_t)THREAD_COUNT * ADDITIONS_PER_THREAD * VALUE_PER_ADDITION; /* All workers have finished, so these reads need no lock. */ printf(\u0026#34;Threads: %d\\n\u0026#34;, THREAD_COUNT); printf(\u0026#34;Additions per thread: %d\\n\u0026#34;, ADDITIONS_PER_THREAD); printf(\u0026#34;Value per addition: %d\\n\u0026#34;, VALUE_PER_ADDITION); printf(\u0026#34;Expected: %\u0026#34; PRIu64 \u0026#34;\\n\u0026#34;, expected); printf(\u0026#34;Actual: %\u0026#34; PRIu64 \u0026#34;\\n\u0026#34;, total); printf(\u0026#34;Result: %s\\n\u0026#34;, total == expected ? \u0026#34;PASS\u0026#34; : \u0026#34;FAIL\u0026#34;); return total == expected ? EXIT_SUCCESS : EXIT_FAILURE; }Notice where total += VALUE_PER_ADDITION is. Between lock and unlock. The read, addition, and write all belong inside the critical section. Locking only around the final write would not protect the whole operation.\ntotal is an ordinary integer. The mutex protects it. The separate atomic start flag holds workers until all threads have been created; it does not protect the addition.\nWe use pthreads to create and join threads. The lock itself still uses our C atomics and x86 PAUSE. Once every pthread_join has returned successfully, the workers are finished and main can read the total without taking the lock.\nCompile and run The complete example is in mutex-atomic (External link). Clone it and run:\ngit clone https://github.com/madhavkhoslaa/mutex-atomic.git cd mutex-atomic gcc -std=c11 -O2 -Wall -Wextra -Wpedantic -Werror main.c -pthread -o mutex-demo ./mutex-demoOutput Threads: 16 Additions per thread: 100000 Value per addition: 5 Expected: 8000000 Actual: 8000000 Result: PASSThe program also returns a failure exit status if the total does not match.\nWhat did the compiler actually generate? [ASSEMBLY AHEAD!] Run these commands from the directory containing mutex.c:\ngcc -std=c11 -O2 -Wall -Wextra -Wpedantic -Werror -c mutex.c -o mutex.o objdump -d -Mintel mutex.o \u0026gt; mutex.asmWe use -c because this file provides the mutex functions, not a main().\nOutput This is the actual GCC 16.2.1 (20260810) output on Linux/x86-64, shown in Intel syntax. I have omitted instruction bytes and alignment NOPs here. The commands above write the complete dump to mutex.asm. The mutex pointer is in rdi.\n0000000000000000 \u0026lt;spin_mutex_lock\u0026gt;: 0: mov eax,0x1 5: xchg DWORD PTR [rdi],eax 7: test eax,eax 9: je 20 \u0026lt;spin_mutex_lock+0x20\u0026gt; 10: mov eax,DWORD PTR [rdi] 12: test eax,eax 14: je 0 \u0026lt;spin_mutex_lock\u0026gt; 16: pause 18: jmp 10 \u0026lt;spin_mutex_lock+0x10\u0026gt; 20: ret 0000000000000030 \u0026lt;spin_mutex_trylock\u0026gt;: 30: xor eax,eax 32: mov edx,0x1 37: lock cmpxchg DWORD PTR [rdi],edx 3b: sete al 3e: ret 0000000000000040 \u0026lt;spin_mutex_unlock\u0026gt;: 40: mov DWORD PTR [rdi],0x0 46: retWhere is the lock prefix on xchg? Memory xchg is implicitly locked. So xchg [rdi], eax already performs an atomic exchange. It puts 1 into the state and the old state into eax. test eax, eax checks whether we got 0.\nThe inner C loop became mov, test, a branch, and pause.\nFor trylock, we get lock cmpxchg. It compares memory with the expected zero in eax. sete al turns the comparison result into our boolean return value.\nUnlock is just a mov? Yes!\nFor a naturally aligned atomic integer in ordinary write-back RAM, x86 already provides the hardware ordering needed for this release store. The C atomic operation also constrains compiler reordering. No mfence is needed here.\nThis does not mean we can replace the C atomic with an ordinary assignment. We still need the C memory-model guarantees. It also does not extend this argument to MMIO or non-temporal stores.\nOne last thing about spinning If the owner gets descheduled, everyone else keeps waiting and consuming CPU. Keep the critical section short. This mutex is non-recursive, must be unlocked by its owner, and does not guarantee fairness. Keep the object naturally aligned and do not copy it while it is in use.\nThe main.c example above passed both an optimized GCC build and a ThreadSanitizer build. In each run, 16 threads completed 1,600,000 protected additions and produced exactly 8,000,000. ThreadSanitizer reported no data races.\nTo run the same check:\ngcc -std=c11 -O1 -g -fsanitize=thread main.c -pthread -o mutex-demo-tsan ./mutex-demo-tsanThe tests are useful evidence. The atomic exchange and acquire/release relationship are why the mutex works.\nSources C atomic exchange (External link) C memory ordering (External link) x86 XCHG instruction reference (External link) x86 PAUSE instruction reference (External link) ","permalink":"https://madhavkhoslaa.github.io/posts/mutex-atomic/","summary":"Writing a spin mutex with C11 atomics, testing it with 16 threads, and looking at the x86-64 assembly.","title":"Writing a Mutex in C: Atomics and x86","topics":["C","Atomics","Mutex","x86","Assembly"],"type":"article"},{"content":"ARP implemented from scratch over raw AF_PACKET sockets in promiscuous mode, with no networking libraries. It covers Ethernet frame parsing and construction, ARP request/reply handling, MAC resolution, the ARP cache, and packet transmission.\nWrite-up: Writing ARP From Scratch and My Learnings\nSource: github.com/madhavkhoslaa/arp-from-scratch (External link)\n","permalink":"https://madhavkhoslaa.github.io/projects/arp-stack/","summary":"ARP from scratch over raw AF_PACKET sockets, in C and Rust.","title":"ARP Stack","topics":[],"type":"project"},{"content":"\nWhat this blog covers and expects you to know. Expectations Know what ethernet frame is like. Know how to use wireshark. endianness Covers Usage of libc on rust What is it and why do we need it? Address Resolution Protocol is used to create a mapping between layer 2(Hardware Address) i.e. Mac Address and layer 3 Address(IP Address). Even though it is specifically used for IP. It is designed to be completely decoupled to the layer 3 protocol.\nSwitch is a layer 2 device and only understands hardware addresses and network applications work layer 3 onwards where you interact with IP/TCP and so on.\nImagine a Scenario You have a Raspberry Pi server in your home to store your files, how does your router(which has an integrated switch) know how we need to send/receive packets from that particular machine when you type 192.168.29.3 on your browser.\nRole of a switch and mac addresses Mac Address: It\u0026rsquo;s a 6-byte hardware address on a device network interface card. First 3 bytes are vendor related and other 3 bytes are specific to device.\nSo your laptop\u0026rsquo;s mac might look like Intel_c3:a9:7e Vendor Bytes + UID\nSwitch is a device that works on routing packets based on mac addresses. A switch has ports where you connect your device to or Wireless. But the broadcast domain remains the same.\n+--------+ | Switch | +---+----+ _________|__________ | | | v v v +----------+ +------------+ +--------+ | Pi Server| | My PC | | Other | | | | (btw Arch) | | PC | +----------+ +------------+ +--------+A switch\u0026rsquo;s job is to forward traffic between your local network based on the mac address and the port where it is connected to, it has zero knowledge about IP.\nHow actual forwarding works Ethernet packets contain the src and dest mac address(Somehow - We will tell about this in a bit) The switch forwards the packet to the particular port. How ARP comes into picture and how we create the mappings of IP and mac. For my Raspberry Pi to send cat images to my mobiles. It needs to know the local IP of my mobile and the mac address of my mobile. For it to create a mapping it needs two steps. 1. A broadcast that says(Who has 192.168.29.3) 2. To reply(Intel_c3:a9:7e is at 192.168.29.3) Two steps of mapping IP to MAC address Step 1: Who(what hardware address) owns this protocol addresses(IP) Step 2: I(hardware address) own this protocol address(IP).\nDo these steps happen always? Try it out on wireshark please? Answer They do not. And the reason being every computer has a cache. Called the ARP table. It stores the responses of a ARP response on your machine. You can clear your ARP table and ping a machine in your local address or just call your default gateway to see how it works on wireshark. Coding our own host Responsibilities of a host When you do not have a hardware address broadcast the need of knowing a hardware address. When you get a broadcast request. Reply to it. When you get a response to a broadcast, store it in your ARP table. Somehow help the switch to know your presence so that it can update its internal mac table. host.rs and boot.rs host.rs Steps Opens a socket: AF_PACKET + SOCK_RAW so the kernel hands us every raw ethernet frame (filtered to ETH_P_ALL). Loop: recv raw frames into a 2048-byte buffer. Skip frames shorter than the 14-byte ethernet header, then keep only ARP frames (EtherType 0x0806 at bytes 12..14). Parse what we need: src MAC (bytes 6..12), sender IP (bytes 28..31), target IP (bytes 38..41). Passive learning - every ARP frame tells us sender_ip -\u0026gt; src_mac, no matter who it is addressed to. We install that mapping into the kernel\u0026rsquo;s ARP table via the SIOCSARP ioctl. Even an ARP request meant for someone else leaves us smarter. Reply - if the frame is a broadcast (ff:ff:ff:ff:ff:ff) asking for our IP, build an ARP reply (opcode 2) and sendto it through a sockaddr_ll so the kernel knows which interface and target MAC to use. boot.rs Reason To populate the mac table in the switch. On boot the host just sends an marker broadcast that it is present at a particular IP. This helps the switch and other host in the network to build a mac table or update the ARP tables.\nSteps Read our own IP and MAC from the interface. Open a raw socket filtered to ETH_P_ARP. Build a gratuitous ARP frame: an unsolicited announcement of \u0026ldquo;my MAC is at my IP\u0026rdquo;, addressed to the broadcast MAC ff:ff:ff:ff:ff:ff (opcode 1, target = ourselves). sendto it once and close the socket. source: host.rs\nThat single frame does a lot of work: the switch learns src MAC -\u0026gt; inbound port, and every other host\u0026rsquo;s ARP cache learns our IP - so nobody ever has to request us. It is the \u0026ldquo;hello, I am here\u0026rdquo; of layer 2.\nFirst experience with endianness While putting the protocol in the filter for the socket(so the kernel hands me only ETH ARP traffic) I passed the value in directly and nothing ever logged. After spending some time, I figured it out. Network byte order is big-endian, but the CPUs I code on are little-endian. The socket compares your filter value against the on-wire ethertype, so the constant has to be converted with .to_be() before it goes to the kernel. The same gotcha bites whenever you hand a multi-byte value to the network. Coding our own switch Responsibilities of a switch Know what mac addresses are connected to what interfaces. Create a map of the interface ID and the mac address. Be able to forward packages to particular ports by reading ethernet headers Learn mac address and interface name passively. Switch.rs Reason A switch is a layer-2 device: it forwards frames purely by MAC address, has no IP and sends no ARP of its own. It is a passive learner - it reads every frame crossing its ports and records src MAC -\u0026gt; inbound port. Real switches keep this map in CAM; ours is just a HashMap.\nSteps Open one raw socket (AF_PACKET, ETH_P_ALL) per port (eth0, eth1, eth2) and put each interface into promiscuous mode (SIOCGIFFLAGS / SIOCSIFFLAGS + IFF_PROMISC) so we see every frame on the wire, not just those addressed to our own MAC. Loop over the ports, recv with MSG_DONTWAIT so one chatty port does not block the others. Keep only ARP (0x0806) and IPv4 (0x0800) frames. Drop multicast but keep broadcast - the docker host\u0026rsquo;s multicast traffic would otherwise storm the loop. Learn - record src MAC (bytes 6..12) -\u0026gt; inbound port in mac_table. Forward by destination MAC: broadcast (ff:ff:ff:ff:ff:ff) -\u0026gt; flood every port except the inbound one unicast with a known dest -\u0026gt; forward out the port that dest was learned on unicast with an unknown dest -\u0026gt; flood the rest (like broadcast), which is also how the switch learns the return path. Docker Networking Setup Topology ┌── net-alice-switch ── alice (10.0.1.10) switch ────┼── net-switch-bob ──── bob (10.0.2.10) └── net-switch-carol ── carol (10.0.3.10)Each docker network has exactly two members, so it behaves as a point-to-point link rather than a shared segment. alice, bob and carol are never on the same network - the only path between any two of them is through the switch container. That forces the switch to have three ports, which is exactly why it needs a real MAC table instead of relaying \u0026ldquo;whatever comes in one port goes out the other\u0026rdquo;.\nHosts alice, bob and carol are Debian containers with iproute2, tcpdump and ping/arping. Each runs the host binary (bind-mounted from arp/target/debug/host) against its eth0. Kernel ARP is disabled on every interface (ip link set eth0 arp off) so the kernel can\u0026rsquo;t answer requests or fill its neighbor cache behind our backs - our implementation owns ARP on the link, exclusively. The docker-assigned IPs (10.0.1.10 / 10.0.2.10 / 10.0.3.10) are the addresses the raw sockets actually operate on. The interface mask is widened to /16 so all three nodes treat each other as directly attached despite sitting on different docker networks. Interfaces get fixed, recognizable MACs (alice 02:a1:1c:e0:00:0a, bob 02:b0:b0:00:00:0b, carol 02:ca:20:10:00:0c) so frames in tcpdump are easy to tell apart. Switch The switch container has three interfaces - eth0, eth1, eth2, one per link, each with its own fixed MAC (02:00:00:00:00:e0/e1/e2) - and runs the switch binary. Kernel ARP is off on the switch ports too. Containers get NET_ADMIN and NET_RAW: the raw sockets and the ioctls (SIOCSARP, promisc-mode) need them. Try it ./docker/up.sh # build binaries + bring topology up docker compose -f docker/docker-compose.yml logs -f switch # watch the switch learn MACs docker exec alice tcpdump -ni eth0 arp # a host\u0026#39;s view of the wire docker exec alice ping -c1 10.0.2.10 # alice resolves bob via OUR ARPWith kernel ARP off, nothing works until your host code resolves neighbours and your switch code forwards frames. Ping failing until you get it right is a pretty good unit test.\n","permalink":"https://madhavkhoslaa.github.io/posts/arpfromscratch/","summary":"Implementing Address Resolution Protocol and Sharing My Learnings on network programming.","title":"Writing ARP From Scratch and My Learnings","topics":["Networking","C","Rust","Internet Protocol","Switch"],"type":"article"},{"content":"A multi-client TCP chat server in Rust. It handles concurrent connections, broadcasts messages, and keeps per-client state, with colored terminal output. Connect with nc 127.0.0.1 5000.\nSource: github.com/madhavkhoslaa/chat-server (External link)\n","permalink":"https://madhavkhoslaa.github.io/projects/tcp-chat/","summary":"A multi-client TCP chat server in Rust.","title":"TCP Chat Server","topics":[],"type":"project"},{"content":"A Redis-compatible key-value server on Rust TCP sockets. It parses the RESP wire protocol and serves concurrent clients. It supports Strings, Hashes, Lists, Sets, TTL and key management commands.\nSource: github.com/madhavkhoslaa/KiloDB (External link)\n","permalink":"https://madhavkhoslaa.github.io/projects/kilodb/","summary":"A Redis-compatible key-value server in Rust.","title":"KiloDB","topics":[],"type":"project"},{"content":"\nScope of this blog To be able to understand what a torrent file does. What does contents inside torrent file do.\nHow you download a torrent. Say you were to download arch linux from the provided torrent file on the archlinux website. You would\nDownload the file from the website load the file onto a torrent client like deluge/transmission/uTorrent etc and it begins downloading. Observing a torrent file Let\u0026rsquo;s disect the Arch Linux Iso (External link) and open the file.\nThe file looks alot gibberish but you can see some ascii charecters like d,i,i with numbers next to them and maybe some urls.\nthis encoding of a file is called bencoding and it is a representation of a object similar to how yaml/json works.\nnow that we know that it is bencoded, we should know how bencoding works.\nWorking of bencoding Opening torrent file in a bencoding viewer tool this tool (External link) helps us open a bencoded file and let\u0026rsquo;s see it in JSON format.\nLet\u0026rsquo;s see the file in JSON which is more human readable format for an object.\n{ \u0026#34;comment\u0026#34;: \u0026#34;Arch Linux 2023.11.01 \u0026lt;https://archlinux.org\u0026gt;\u0026#34;, \u0026#34;created by\u0026#34;: \u0026#34;mktorrent 1.1\u0026#34;, \u0026#34;creation date\u0026#34;: 1698821953, \u0026#34;info\u0026#34;: { \u0026#34;length\u0026#34;: 846540800, \u0026#34;name\u0026#34;: \u0026#34;archlinux-2023.11.01-x86_64.iso\u0026#34;, \u0026#34;piece length\u0026#34;: 524288, \u0026#34;pieces\u0026#34;: \u0026#34;\u0026lt;hex\u0026gt;27 51 12 5C 1A DB 1B......Ommited for ease.....\u0026lt;/hex\u0026gt;\u0026#34; }, \u0026#34;url-list\u0026#34;: [ \u0026#34;https://mirror.aarnet.edu.au/pub/archlinux/iso/2023.11.01/\u0026#34;, \u0026#34;https://mirrors.rit.edu/archlinux/iso/2023.11.01/\u0026#34;, \u0026#34;https://ftp.ek-cer.hu/pub/mirrors/ftp.archlinux.org/iso/2023.11.01/\u0026#34;, \u0026#34;https://ftp.heanet.ie/mirrors/ftp.archlinux.org/iso/2023.11.01/\u0026#34;, \u0026#34;https://mirror.puzzle.ch/archlinux/iso/2023.11.01/\u0026#34;, \u0026#34;https://mirror.csclub.uwaterloo.ca/archlinux/iso/2023.11.01/\u0026#34;, . . . // Omitted for ease . . . ] }","permalink":"https://madhavkhoslaa.github.io/draft/torrent_file/","summary":"All of us have downloaded torrent files and files using those torrent files, but probably most of us have not even opened a .torrent file and observe how it works","title":"Torrent: 1. How a .torrent file works","topics":["Bittorrent","torrent"],"type":"article"},{"content":"\nErrors in any language are hard to handle since they include the caller, defining errors, handling them and reporting to the end user. In this blog I will explain how to handle and create errors in rust.\nTypes of Errors Unrecoverable Errors Recoverable Errors How to handle unrecoverable Errors. These errors are errors that make you want to stop the execution of the program. Like not being able to find a free port for starting the server, division by zero. These errors use the panic macro to stop the execution of the program in rust.\nExample of panic macro. fn main(){ let a = 5; let b = 0; let c = divide(a, b); println!(\u0026#34;{}\u0026#34;, c); } fn divide(a: u8, b: u8) -\u0026gt; u8{ if(b == 0){ panic!(\u0026#34;Division by zero not allowed\u0026#34;); } return a/b; }The result of this code will be a panic with a message Division by zero not allowed.\nHow to handle recoverable errors These errors are to be handled at runtime and do not stop the execution of the program. Like an empty file in a directory. Functions that expect to return a success value or an erroneous value use the Result enum to wrap the values.\nenum Result\u0026lt;T, E\u0026gt;{ Ok(T), Err(E) }Example of using a Result enum fn main(){ let data = get_database_contents(2); let data = match data{ Ok(value) =\u0026gt; {value}, Err(value) =\u0026gt; {value} }; println!(\u0026#34;{}\u0026#34;, data); } fn get_database_contents(id: usize) -\u0026gt; Result\u0026lt;String, String\u0026gt;{ if id == 1{ return Ok(\u0026#34;Database success\u0026#34;.to_owned()); } else { return Err(\u0026#34;Database fail\u0026#34;.to_owned()); } }Here in the mock function, we return the Ok variant of the enum if the argument is 1 and return the Error variant of the enum in other cases; The caller of the function has the responsibility of handling the enum variants.\nSyntactical sugar for early returning of errors using the ? operator. use std::fs::File; use std::io::{self, Read}; fn read_file(path: \u0026amp;str) -\u0026gt; Result\u0026lt;String, io::Error\u0026gt; { let mut file = match File::open(path) { Ok(file) =\u0026gt; file, Err(error) =\u0026gt; return Err(error), }; let mut contents = String::new(); match file.read_to_string(\u0026amp;mut contents) { Ok(_) =\u0026gt; Ok(contents), Err(error) =\u0026gt; Err(error), } } fn main() { let file_path = \u0026#34;path/to/your/file.txt\u0026#34;; match read_file(file_path) { Ok(contents) =\u0026gt; println!(\u0026#34;File contents:\\n{}\u0026#34;, contents), Err(error) =\u0026gt; eprintln!(\u0026#34;Error reading file: {}\u0026#34;, error), } }Using Syntactical sugar for returning errors.\nuse std::fs::File; use std::io::Read; fn read_file(path: \u0026amp;str) -\u0026gt; Result\u0026lt;String, std::io::Error\u0026gt; { let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(\u0026amp;mut contents)?; Ok(contents) fn main() { let file_path = \u0026#34;path/to/your/file.txt\u0026#34;; match read_file(file_path) { Ok(contents) =\u0026gt; println!(\u0026#34;File contents:\\n{}\u0026#34;, contents), Err(error) =\u0026gt; println!(\u0026#34;Error reading file: {}\u0026#34;, error), } }Returning different types of errors. Sometimes you might want to send different types of errors to the function caller in case different errors arise in the execution of the function. Here we have two approaches for the following.\nDynamic dispatch approach. In this case, we can use dynamic dispatch on the error type and send different types of errors.\nfn do_operation(arg: \u0026amp;str) -\u0026gt; Result\u0026lt;String, Box\u0026lt;dyn error::Error\u0026gt;\u0026gt;{ if boolean_response() { Ok() } if boolean_response_for_error1() { Err(Box::new(ErrorType1)) } if boolean_response_for_errore() { Err(Box::new(ErrorType2)) } }Issue with this approach of using dynamic dispatch On the runtime we do not know what error type the caller of the function will receive on the runtime hence the error handling will be hard to handle.\nUsing Error Enums approach. We can create an enum with all the values for the enums and then create a match of all the values for the enum.\n#[derive(PartialEq, PartialOrd)] enum APP_FAILURE { TIMEOUT, NULL_VALUE, OTHER_ERROR } fn main (){ let Result = result_returning_function(); let Result = match Result { Ok(value) =\u0026gt; {value}, Err(value) =\u0026gt; { if(value == APP_FAILURE::TIMEOUT){ //Do something } // .. exhaustive comparison list } } } fn result_returning_function(filename: \u0026amp;str) -\u0026gt; Result\u0026lt;String, APP_FAILURE\u0026gt; {}","permalink":"https://madhavkhoslaa.github.io/archive/errors/","summary":"Errors are an inevitable part of any software development process, and Rust provides a robust mechanism for managing and handling them. This blog aims to demystify Rust\u0026rsquo;s error handling philosophy, covering its unique features, error types, and various strategies for effectively dealing with errors.","title":"Error handling in Rust.","topics":["rust","erorrs"],"type":"article"},{"content":"Iterator Design Pattern, What is it? Example 1 Let\u0026rsquo;s consider you are this tiny person in the image and you have to walk traverse the tree below. Ideally, how you traverse it, it does not matter what is the most important thing is 1. where you are and 2. Where you want to be when you go to the next node.\nSo in a way, an iterator design pattern(in real life) is a thought process of how you iterate a structure(not just a tree), or you know where you want(not how) to go next.\nIt is the way to decide if you go on the left node or the right, or the leaf node of the left subtree or the leaf node of the rightmost subtree but not how you do it.\nExample 2 Imagine you are the Pacman ghost and the representation of the Pacman world is in a graph. Which node you go to is dependent on where Pacman is. Imagine you are on a T intersection and Pacman is on the right side of the T intersection. Then the next node you want to traverse is the right node.\nWhy did I highlight next ? | Let\u0026rsquo;s talk Code. Iter trait This\ntrait Iterator { type Item; fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Self::Item\u0026gt;; }I will talk about the iter trait below in the blog once I get done with explaining how to use pre-built iterators.\nIterators in Rust Lazy Iterator example\nlet v: Vec\u0026lt;u8\u0026gt; = vec![1,2,3,4]; let vec_iter = v.iter();Iterating a List using iterators Using lazy iterator to iterate\nExample 1 (Immutable iterator) The value of next sends a reference to the value in the data structure. In our case, we create an instance of the iterator called vec_iter. Then use it to loop through our list. The iterator\u0026rsquo;s next trait is implemented in the for in loop in rust.\nfn main() { let v: Vec\u0026lt;u8\u0026gt; = vec![1,2,3,4]; let vec_iter = v.iter(); for vec_val in vec_iter{ println!(\u0026#34;{}\u0026#34;, vec_val); } }Output\n1 2 3 4Example 1 (Mutable iterator) For example, if you want to increment the value of the item in the data structure through the loop you need a mutable reference to the value in the data struct. Creating an iterator with iter_mut returns mutable references to the value in the loop.\nfn main() { let mut v: Vec\u0026lt;u8\u0026gt; = vec![1,2,3,4]; println!(\u0026#34;{:?}\u0026#34;, v); let vec_iter = v.iter_mut(); for vec_val in vec_iter{ *vec_val = *vec_val + 1; } println!(\u0026#34;{:?}\u0026#34;, v); }Output\n[1, 2, 3, 4] [2, 3, 4, 5]Producers of iterators These are methods that attach to iterators to produce more iterators. Popular examples of these \u0026ldquo;Producers\u0026rdquo; are 1. Map 2. Filter. Also, I assume if you\u0026rsquo;re reading this blog you are aware of the concepts of map and filter.\nA map creates an iterator that you can attach other iterators (and so on\u0026hellip;). An obvious question to this is how do you resolve these chained iterators to get a value? This leads us to the next topic\nConsumers of iterators Consumers are methods like collect the take in chained iterators and run it one by one on data that each producer resolves to.\nIterator1 =\u0026gt; Data =\u0026gt; Iterator 2 =\u0026gt; Data =\u0026gt; Iterator 3 =\u0026gt; Data\n^This what a consumer does.\nExample of how to use producers and consumers of iterators Let\u0026rsquo;s try to create the above example of iterating to increase the value of elements in an array using Map.\nfn main() { let x: Vec\u0026lt;u8\u0026gt; = vec![1, 2, 3, 4]; let y: Vec\u0026lt;u8\u0026gt; = x.iter().map(|x| x + 1).collect(); // ^---Producer ^----------------Consumer println!(\u0026#34;{:?}\u0026#34;, y); }Output\n[2, 3, 4, 5]Using multiple producers fn main() { let vec: Vec\u0026lt;u8\u0026gt; = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let vec2: Vec\u0026lt;u8\u0026gt; = vec.iter().map(|x| x / 2).map(|x| x + 10).filter(|x| x%2==0).collect(); // Divides all numbers by 2 into a u8 then adds 10 to everything. Then filters out even numbers println!(\u0026#34;{:?}\u0026#34;, vec2); }The three forms of iteration iter(), which iterates over \u0026amp;T. iter_mut(), which iterates over \u0026amp;mut T. into_iter(), which iterates over T. Revisiting the Iter trait trait Iterator { type Item; fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Self::Item\u0026gt;; }calling next will return an Option\u0026lt;Self::item\u0026gt; as long as there are elements in the collection.\nExample of implementing Iter trait to create own iterator for our data structure. struct Counter { pub limit: usize, count: usize, } impl Counter { fn new(limit: usize) -\u0026gt; Counter { Counter { limit, count: 0 } } } impl Iterator for Counter { type Item = usize; fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Self::Item\u0026gt;{ if self.count \u0026lt;= self.limit { self.count += 1; return Some(self.count); } None } } fn main() { let mut c = Counter::new(10); let mut start = Some(0); loop { if start == None { break; } println!(\u0026#34;{}\u0026#34;, start.unwrap()); start = c.next(); } }Output\n0 1 2 3 4 5 6 7 8 9 10 11How to create an iterator to traverse a tree using for loops !!!! [FUN CODE AHEAD!] // More than two derives // generics // Deref coercion #[derive(Debug, Clone)] struct Node { left: Option\u0026lt;Box\u0026lt;Node\u0026gt;\u0026gt;, right: Option\u0026lt;Box\u0026lt;Node\u0026gt;\u0026gt;, value: u8, } struct PreTreeIterator { head: Node, iter_list: Vec\u0026lt;u8\u0026gt;, current_value: Option\u0026lt;u8\u0026gt;, ctr: usize, } impl PreTreeIterator { pub fn new(head: \u0026amp;Node) -\u0026gt; PreTreeIterator { PreTreeIterator { head: head.clone(), iter_list: vec![], current_value: None, ctr: 0, } } pub fn generate_iter_list(\u0026amp;mut self, head: \u0026amp;Node) { match \u0026amp;head.left { Some(x) =\u0026gt; { self.generate_iter_list(x); } None =\u0026gt; {} } self.iter_list.push(head.value); match \u0026amp;head.right { Some(x) =\u0026gt; { self.generate_iter_list(x); } None =\u0026gt; {} } } } impl Iterator for PreTreeIterator { type Item = u8; fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Self::Item\u0026gt; { if self.ctr \u0026lt; self.iter_list.len() { let to_return = *self.iter_list.get(self.ctr).unwrap(); self.ctr += 1; return Some(to_return); } else { None } } } fn main() { let tree = Node { left: Some(Box::new(Node { left: None, right: Some(Box::new(Node { left: None, right: None, value: 3, })), value: 2, })), right: Some(Box::new(Node { left: None, right: None, value: 9, })), value: 1, }; println!(\u0026#34;{:?}\u0026#34;, tree); let mut iter = PreTreeIterator::new(\u0026amp;tree); println!(\u0026#34;{:?}\u0026#34;, iter.iter_list); iter.generate_iter_list(\u0026amp;tree); println!(\u0026#34;{:?}\u0026#34;, iter.iter_list); println!(\u0026#34;Looping through the tree in preiterator way using iterator design pattern\u0026#34;); loop { let value = iter.next(); if value == None { break; } println!(\u0026#34;{:?}\u0026#34;, value.unwrap()); } // Node { left: Some(Node { left: None, right: Some(Node { left: None, right: None, value: 3 }), value: 2 }), right: Some(Node { left: None, right: None, value: 9 }), value: 1 } }Output [] [2, 3, 1, 9] Looping through the tree in pre-order way using the iterator design pattern 2 3 1 9 Sources\n","permalink":"https://madhavkhoslaa.github.io/posts/rustiter/","summary":"Creational design patterns aid in the instantiation of objects by providing suitable abstractions and make the systems independent of how objects are created composed and represented.","title":"Functional Programming in Rust 2: Iterators","topics":["Design Patterns","Iterator Design Pattern","Rust","Blazingly Fast","loops","Not C++"],"type":"article"},{"content":"\nWhat is Functional Programming? It is a Programming Paradigm where you use functions to solve what you are building. The most common features that you might have used FP in any language will be Map and Reduce. Where you pass in a function what iterates in the collection you run map or reduce on.\nRust has some functional features too that I plan to write here.\nClosures Let\u0026rsquo;s do a quick Google search \u0026ldquo;closures\u0026rdquo;; The first result that comes to me is from MDN.\nLet\u0026rsquo;s see what MDN has to say(even though it is JS).\nA closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.\nWhat information can we take from this statement? It is a function that is inside a function The inner function shares the same environment as the outer function. What information we can conclude from this statement? The variables defined in the outer function are accessible in the inner function. There is a lot of information that is left here in the context of rust, but hold on for a while :D\nSyntax for closure in Rust. Take a look carefully at how you can create closures in rust.\nYou give no type signatures for the formal arguments. let a = |arg| arg + 1; You give no return type signatures for the closure. let a = |arg: u8| arg + 1; You give types for the formal arguments and the return signatures. let a = |arg: u8| -\u0026gt; u8 {arg + 1} So closures in rust are kind of \u0026ldquo;Pythonic\u0026rdquo; in a way where you can choose to give function type signatures, Right? Yes, but to an extent. Look at this example\nPlaying with Closures !!!! fn main() { let example = |arg| arg + 10; // ^ This closure has no types for the arg, it just adds 10 to any type that supports addition(We can assume the minimum requirement of the type here) let x: usize = 10; let y: u8 = 11; example(x); // Called with type \u0026#34;usize\u0026#34; example(y); // Called with type \u0026#34;u8\u0026#34; }You might expect this program to compile correctly and on runtime to print 21 and 21.\nBut we received a compile error.\nOutput\nerror[E0308]: mismatched types --\u0026gt; src/main.rs:10:13 | 10 | example(y); | ------- ^ expected `usize`, found `u8` | | | arguments to this function are incorrect | note: closure defined here --\u0026gt; src/main.rs:6:19 | 6 | let example = |arg| arg + 10; | ^^^^^ help: you can convert a `u8` to a `usize` | 10 | example(y.into()); | +++++++ For more information about this error, try `rustc --explain E0308`. error: could not compile `playground` due to previous errorWhen we dissect the error(Rust has the best errors btw) we see that on the second time the compiler says that \u0026ldquo;expected usize found u8\u0026rdquo; and then gives a suggestion.\nSo we learn that closure takes the first arguments type and return type as the type signature of the closure.\nPlaying with closures(again) fn main() { let b = 1; // defined in function main let a = |c: u8| -\u0026gt; u8 {c + b}; // closure a uses b to add to the argument let z = a(1); // Result is assigned to z. println!(\u0026#34;{}\u0026#34;,z); }Output\n2We can observe here that variable b is accessible inside of closure a, so we can conclude that a closure shares the same scope as the parent.\nPhew, a lot of stuff to learn. Let\u0026rsquo;s break down what I wrote down Closures are functions that are inside a function and hence share the same scope as the parent function. They are \u0026ldquo;Pythonic\u0026rdquo; in a way that they do not need type signatures but instead the compiler gives it a signature based on the type of arguments on the first call. Moving values inside closures. If you\u0026rsquo;re reading this blog I expect you to know what a move in rust is. If you don\u0026rsquo;t you can read this (External link).\nSo there are three ways of moving values into closues.\nImmutable immutable Mutable argument Borrowing Example of Immutable arguments fn main() { let list = vec![1, 2, 3]; let only_borrows = |list| println!(\u0026#34;From closure: {:?}\u0026#34;, list); println!(\u0026#34;Before calling closure: {:?}\u0026#34;, list); only_borrows(list); println!(\u0026#34;After calling closure: {:?}\u0026#34;, list); }Output\nCompiling playground v0.0.1 (/playground) error[E0382]: borrow of moved value: `list` --\u0026gt; src/main.rs:8:45 | 2 | let list = vec![1, 2, 3]; | ---- move occurs because `list` has type `Vec\u0026lt;i32\u0026gt;`, which does not implement the `Copy` trait ... 7 | only_borrows(list); | ---- value moved here 8 | println!(\u0026#34;After calling closure: {:?}\u0026#34;, list); | ^^^^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0382`. error: could not compile `playground` due to previous errorExample of Mutable arguments fn main() { let mut list = vec![1, 2, 3]; let only_borrows = |mut list: Vec\u0026lt;u8\u0026gt;| { list.push(4); println!(\u0026#34;From closure: {:?}\u0026#34;, list ); list }; println!(\u0026#34;Before calling closure: {:?}\u0026#34;, list); list = only_borrows(list); println!(\u0026#34;After calling closure: {:?}\u0026#34;, list); }Output\nBefore calling closure: [1, 2, 3] From closure: [1, 2, 3, 4] After calling closure: [1, 2, 3, 4]Example of borrowing fn main() { let mut list: Vec\u0026lt;u8\u0026gt; = vec![1, 2, 3]; let only_borrows = |list: \u0026amp;mut Vec\u0026lt;u8\u0026gt;| { list.push(4); println!(\u0026#34;From closure: {:?}\u0026#34;, list ); }; println!(\u0026#34;Before calling closure: {:?}\u0026#34;, list); only_borrows(\u0026amp;mut list); println!(\u0026#34;After calling closure: {:?}\u0026#34;, list); }Output\nBefore calling closure: [1, 2, 3] From closure: [1, 2, 3, 4] After calling closure: [1, 2, 3, 4]Moving captured values out of fn traits So, once a value or a reference has been passed to the closure, The closure decides what happens with it. It could use the value once and drop it, mutate the value or can take references to a value and do all of these.\nWhy do we have to do this? Since Rust has this philosophy of all errors being compiled time errors and not runtime errors, we need to know how the values are being handled inside the closure so that in the runtime we do not get any UBs.\nThis defines three types of closures traits when they are passed in functions or structs.\nFnOnce -\u0026gt; Uses the value which is passed in it and drops it FnMut -\u0026gt; It can modify the passed values Fn -\u0026gt; Can take any reference Example of FnOnce Consider the rust option enum. Rust option enum has a unwrap_or_else function that returns Some value or runs the closure to provide the value for.\nLet\u0026rsquo;s dissect the code below.\nimpl\u0026lt;T\u0026gt; Option\u0026lt;T\u0026gt; { pub fn unwrap_or_else\u0026lt;F\u0026gt;(self, f: F) -\u0026gt; T where F: FnOnce() -\u0026gt; T { match self { Some(x) =\u0026gt; x, None =\u0026gt; f(), } } }Observations The function returns a type T which is the value held by the option enum There is a trait bound f which is FnOnce and returns the same type as the enum. So we see can conclude that we need to pass in a closure that takes in no arguments to unwrap_or_else and return the same value as the option and it has to run only once.\nExample of FnMut pub fn sort_by_key\u0026lt;T, F, K\u0026gt;(slice: \u0026amp;mut [T], key_fn: F) where F: FnMut(\u0026amp;T) -\u0026gt; K, K: Key, let mut friends = [\u0026#34;Punchy\u0026#34;, \u0026#34;Isabelle\u0026#34;, \u0026#34;Sly\u0026#34;, \u0026#34;Puddles\u0026#34;, \u0026#34;Gladys\u0026#34;]; // sort by the length of the string in bytes radsort::sort_by_key(\u0026amp;mut friends, |s| s.len()); assert_eq!(friends, [\u0026#34;Sly\u0026#34;, \u0026#34;Punchy\u0026#34;, \u0026#34;Gladys\u0026#34;, \u0026#34;Puddles\u0026#34;, \u0026#34;Isabelle\u0026#34;]);Example of Fn fn main() { fn call_with_one\u0026lt;F\u0026gt;(func: F) -\u0026gt; usize where F: Fn(usize) -\u0026gt; usize { func(1) } let double = |x| x * 2; let x = call_with_one(double); // Does not mutate the value, returns the double println!(\u0026#34;{}\u0026#34;,x); }Output\n2","permalink":"https://madhavkhoslaa.github.io/posts/functionalrust/","summary":"It is a Programming Paradigm where you use functions to solve what you are building. The most common features that you might have used FP in any language will be Map and Reduce. Where you pass in a function what iterates in the collection you run map or reduce on.","title":"Functional Programming in Rust 1: Closures","topics":["Rust","C++","Functional Programming","Closures","Traits","Ferris","Blazingly Fast"],"type":"article"},{"content":"Ownership is one of the features that make it different than so many programming languages and what makes it memory safe and a good language for concurrent programming. It solves problems like null pointers, dangling pointers and data races without a garbage collector at the run time.\nUnderstanding Rust Ownership How is data stored in the program? Stack Heap Data(variables in function arguments and defined) in the stack have a known type hence the size of the data in the stack is known at the time of compilation. However, data types like vectors and strings are bound to grow in size hence they are stored in the heap(their size cannot be determined at the time of compilation).\nThe image above shows the organization of a stack.\nWhat is ownership? Rust aims to solve problems like dangling pointers in languages like C++(CPP Slander (External link)).\nProblem 1\nNode* a = new Node(); some_funtion(a); *a-\u0026gt;next = new Node() void some_funtion(Node* node){ delete a; }Problem 2\nsome_function(){ for(int i = 0; i\u0026lt;100; i++){ Node* n = new Node(); } }Problems like these in rust are rare and you know when you are making those.\nOwnership is how rust compiler managers data in the heap safely without a garbage collector at the compile time.\nRules of ownership Each value in Rust has an owner. There can only be one owner at a time. When the owner goes out of scope, the value will be dropped. Source: Rust Lang Book (External link)\nWhat is value? fn main(){ let a = String::from(\u0026#34;Hello World\u0026#34;); // ^ Owner ^Value }Understanding ownership through real-life examples Scenario 1 Assume you have a laptop. Your friend wants to borrow your laptop fn main(){ let madhav = String::from(\u0026#34;Madhav\u0026#39;s Laptop\u0026#34;); // Madhav is the owner and he owns the string \u0026#34;Madhav\u0026#39;s Laptop\u0026#34;; let diya = madhav; println!(\u0026#34;Diya = {}, Madhav = {}\u0026#34;, madhav, diya); }relating to other languages like c++, you might expect that variable named diya will have the value of the string, and the variable madhav will have it too? However, the output says something different. Output:\nerror[E0382]: borrow of moved value: `madhav` --\u0026gt; src/main.rs:5:40 | 2 | let madhav = String::from(\u0026#34;Madhav\u0026#39;s Laptop\u0026#34;); | ------ move occurs because `madhav` has type `String`, which does not implement the `Copy` trait 3 | // Madhav is the owner and he owns the value \u0026#34;Madhav\u0026#39;s Laptop\u0026#34;; 4 | let diya = madhav; | ------ value moved here 5 | println!(\u0026#34;Diya = {}, Madhav = {}\u0026#34;, madhav, diya); | ^^^^^^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0382`.Just like in reality if madhav gives his laptop to diya he will not have the laptop while the laptop is with diya, right?\nExplaination (Read #Rules of ownership 1. and 2.). These rules state that for a value there can be only one owner. In the case above there were two owners at the same time. And we got an error saying borrow of moved value: madhav when we tried printing the value of the variable madhav.\nSo what is a move in rust? let a = String::from(\u0026#34;Some string\u0026#34;); let b = a; // This is a move in other languages, b might copy the value of a or point to the same location as a. but in rust, there can be only one owner to a value as we studied above. So what happens is b takes the ownership of the value that a point towards.\nHow does diya gets madhav\u0026rsquo;s laptop? fn main(){ let madhav = String::from(\u0026#34;Madhav\u0026#39;s Laptop\u0026#34;); // Madhav is the owner and he owns the string \u0026#34;Madhav\u0026#39;s Laptop\u0026#34;; let diya = \u0026amp;madhav; println!(\u0026#34;Diya = {}, Madhav = {}\u0026#34;, madhav, diya); }Here the variable diya borrows the value from the variable madhav.\nOutput\nDiya = Madhav\u0026#39;s Laptop, Madhav = Madhav\u0026#39;s LaptopScenario 2 Assume you have a Laptop and you need to overclock it. Your friend borrows your laptop and overclocks it. Friend returns the laptop to you madhav variables pass his laptop to the function overclock_laptop() and expects laptop string to be appended with \u0026ldquo;+ 2Ghz\u0026rdquo; but what does happen?\nfn main(){ let madhav = String::from(\u0026#34;Madhav\u0026#39;s Laptop\u0026#34;); // Madhav is the owner and he owns the string \u0026#34;Madhav\u0026#39;s Laptop\u0026#34;; let madhav = overclock_laptop(madhav); println!(\u0026#34;{}\u0026#34;, madhav) } fn overclock_laptop(mut laptop: String) -\u0026gt; String { laptop.push_str(\u0026#34; + 2Ghz\u0026#34;); return laptop; }output\nMadhav\u0026#39;s Laptop + 2GhzThis code works fine, but there\u0026rsquo;s a tiny inconvenience here. The inconvenience is that we have to pass around ownership from functions to variables again and again at our point you will have to return tuples and it can get messy.\nBorrowing in function Here we create a mutable variable madhav we pass in the mutable reference to the function overclock_laptop, where the variable is mutated, Notice here we did not have to switch ownership multiple times, since the function borrowed the value once and it got reflected in the variable in the function main.\nfn main(){ let mut madhav = String::from(\u0026#34;Madhav\u0026#39;s Laptop\u0026#34;); overclock_laptop(\u0026amp;mut madhav); println!(\u0026#34;{}\u0026#34;, madhav); } fn overclock_laptop(laptop: \u0026amp;mut String) { laptop.push_str(\u0026#34;+ 2Ghz\u0026#34;); }Output:\nMadhav\u0026#39;s Laptop + 2GhzWhat is stored in the heap? String Vector Structs - A struct could have alot of fields and making a decision to copy the values would be cost inefficient and a borrowed reference will be better.. ","permalink":"https://madhavkhoslaa.github.io/archive/rustownership/","summary":"Ownership is one of the features that make it different than so many programming languages and what makes it memory safe and a good language for concurrent programming. It solves problems like null pointers, dangling pointers and data races.","title":"Understanding Rust Ownership","topics":["Rust","Ownership","Not C++","Blazingly Fast","Ferris","Borrowing","heap","stack","pointers"],"type":"article"},{"content":"Automating your Linux Development using Ansible and GNU Stow ","permalink":"https://madhavkhoslaa.github.io/draft/ansible/","summary":"Automating your Linux Development using Ansible and GNU Stow","title":"Automating your Linux Development using Ansible and GNU Stow","topics":["ansible","dotfiles","Linux","GNU","stow","btw i use arch"],"type":"article"},{"content":"What is a URL shortner ? It is a service that takes in a larger URL and turns it into a smaller URL, for the sake of:\nConvencice Tracking clicks Tracking where the URL was opened from. For Example:\nLong URL: https://www.amazon.in/gp/bestsellers/?ref_=nav_em_cs_bestsellers_0_1_1_2 Short URL: http://MyWebSite.com/{key}\nWe replaced the larger url with a unique key in out system from which we can identify our unique key.\nHow to generate a key ? You need a way to transform your string to an other string, it could be either a set of random charecters or using a hashing algorithm. You use a hashing function to create a key that does not exist our system and is unique to a (long url) or (long url + user)\nWhat is hashing and a hash function ? hasing is the process of transforming a string to another key to a shorter representation, this is used in python to implement dictionaries which are also called \u0026ldquo;hash tables\u0026rdquo;.\nA hashing function is a function that transforms a larger string to a smaller one. A good hashing function is a function in which no two strings have the same hash.\nHashing function Example:\ndef hash(key_str): return key_str[0:3] + \u0026#34;abc\u0026#34; print(hash(\u0026#34;google.com\u0026#34;)) // ooabc print(hash(\u0026#34;googgggle.com\u0026#34;)) // ooabcCollision Hash Let\u0026rsquo;s observe our hashing function. This hasing function is not the best function becasue two different URLs have exactly the same hash. The scenario when we have one same hash for two completely different inputs is called a hash collision. A good hasing algorithm has very less hash collisions.\nGood enough hasing algorithm key_value_store = {} PREFIX=\u0026#34;lmn\u0026#34; POST_FIX=\u0026#34;abc\u0026#34; def hash_url(long_url): curr_hash = basic_hash(long_url) if(curr_hash in key_value_store): hash_url = POST_FIX + long_url + PREFIX curr_hash = basic_hash(hash_url) key_value_store[curr_hash] = long_url return curr_hash def basic_hash(long_url): return long_url[0:4] + \u0026#34;xyz\u0026#34; hash_url(\u0026#34;google.com\u0026#34;) hash_url(\u0026#34;goooogle.com\u0026#34;) hash_url(\u0026#34;gooooglee.com\u0026#34;) print(key_value_store) # {\u0026#39;googxyz\u0026#39;: \u0026#39;google.com\u0026#39;, \u0026#39;goooxyz\u0026#39;: \u0026#39;goooo2gle.com\u0026#39;, \u0026#39;abcgxyz\u0026#39;: \u0026#39;goooo3glee.com\u0026#39;}Base 62 Hashing How do we get indexed for URLs ? How to store shortened URLs and large URLs ? How to redirect URLs ? HTTP 30x Codes Storing clicks and information about it. ","permalink":"https://madhavkhoslaa.github.io/draft/urlshortner/","summary":"How to make a URL shortner","title":"How to make a URL shortner","topics":["System Design","Hashing","Base62","Bloom Filter","SQL","MongoDB","Redis","Redirect"],"type":"article"},{"content":"Static and Dynamic Dispatch in Rust summary: \u0026ldquo;Static and Dynamic Dispatch in Rust\u0026rdquo; ","permalink":"https://madhavkhoslaa.github.io/draft/dyn/","summary":"Static and Dynamic Dispatch in Rust","title":"Static and Dynamic Dispatch in Rust","topics":["Design Patterns","Iterator Design Pattern","Rust","Blazingly Fast","loops","Not C++"],"type":"article"},{"content":"Creational Design Patterns Creational design patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code (External link).\nWhy were they made and why should you care about reading them? You must have noticed in a larger code base where multiple classes dealing with the class instantiation becomes too hard, in fact in the case of languages like java where the ecosystem expects everything to be a class; a framework called spring had been developed to solve the problem of instantiating classes(and so much more). So you get the scope of the problem of just the creation of objects.\nCreational design patterns aid in the instantiation of objects by providing suitable abstractions and make the systems independent of how objects are created composed and represented.\nThey are important when a system grows large and require how objects are constructed rather than what objects are constructed. They are a step above a normal hardcoded instantiation like const instance = new SomeClass()\nThe Builder Design Pattern Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.\nReal-life example Have you ever been to an ice cream parlor where you are handed a sheet of paper and you tick on stuff and then create that ice cream for you? Or if not it\u0026rsquo;s similar to a subway shop where you select what you want in the sandwich. The kind of bread you want, and the kind of sauce are just the parameters you pass onto the sandwhich class or the ice cream class.\nThe Problem: Have you encountered a situation where a class requires so many arguments to be instantiated and some of them are optional too? The instantiation usually goes something like this const instance = new ClassA(arg1, arg2? arg3, arg4, arg5, arg6); and you can relate how easy it is to forget the order of the arguments and what arguments the class requires in the first place. So there is a clear problem of the constructor being too large and complex, there can also be situations where the constructors are overloaded many times as well leading to even more confusion.\nProposal: How about you create a method where you pass in arguments in a more visual way, such that you are aware of what you are passing and such that they provide an easy way to construct objects.\nHow: You create multiple static variables holding values for the variables you are going to pass in the constructor, you create setter methods for these static variables with static methods, and create a getInstance method where you pass in the static variables and return an instance of the class. You can also choose to keep the constructor private if you want your instantiation only from the builder static methods.\nBuilder methods provide a piecewise construction of an object\nCode Example Suppose you need to have an object of type car in your application, maybe for an ORM. You need to create it piecewise and in a verbose manner in your system. This is how you will do it.\nclass Car{ static _color: string; static _pistons: number; static _wheelbase: number; static _length: number; public color: string; public pistons: number; public wheelbase: number; public length: number; private constructor(color: string, pistons: number, wheelbase: number, length: number){ this.color = color; this.pistons = pistons; this.wheelbase = wheelbase; this.length = length; } static setColor(color: string){ this._color = color; return this; } static setPistons(pistons: number){ this._pistons = pistons; return this; } static setWheelbase(wheelbase: number){ this._wheelbase = wheelbase; return this; } static setLength(length: number){ this._length = length; return this; } static getCar(){ // Logic for checking values and passing in default values; return new Car(this._color, this._pistons, this._wheelbase, this._length); } } const RedFerrari: Car = Car.setColor(\u0026#34;Red\u0026#34;) .setPistons(12) .setLength(2100) .setWheelbase(40) .getCar(); console.log(RedFerrari) /** Car: { \u0026#34;color\u0026#34;: \u0026#34;Red\u0026#34;, \u0026#34;pistons\u0026#34;: 12, \u0026#34;wheelbase\u0026#34;: 40, \u0026#34;length\u0026#34;: 2100 } **/Code Explanation There are many static variables that you set from the static methods, these static methods return this so that you can use a chain of these methods. In the end, the getCar or an equivalent build function calls the constructor of the class and returns the fully created instance.\nSomething Cool (Creating HTML from Code and builder design pattern) While studying builder design patterns, I was able to create methods to generate HTML pages through code. I created an HTML class and an array that held the tags and the content for the tags. Every static function for header and paragraph took in the string values and appended them to the metadata static array. At the end when the build method is called it parses the static metadata array into a string that represents HTML. I was able to create an HTML page by just invoking functions on this object.\nHTML Page Generated From The Above Code Factory Design Pattern Factory Method is a creational design pattern that provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.\nReal-life example Remember how we had the option to create a custom ice cream in the builder design pattern? It might be daunting for you to think about what you want at the store or maybe seeing so many options makes you anxious? If that\u0026rsquo;s so; you order something from the menu itself, you don\u0026rsquo;t have to talk to the server and just tell him what you want without telling him the specifics of what you want. An example of this is just the menu.\nThe Problem: Sometimes there are a lot of objects of the same type and kind that have to be created and sometimes those objects can be builder classes too and the instantiation of a builder object is too ceremonial, piecewise, and takes time. To tackle this problem of creating objects piecewise; factory design patterns were made to create objects in a wholesale manner. You just invoke a particular function with a verbose name of what object you want and get the instance of the object. For example, if you have a builder class for cars, you gave call a lot of methods to just create an instance, but in the case of a factory design, you can create a function with an appropriate name that returns the same object every time you invoke it.\nProposal: You create an abstraction where you receive objects of the same type base on what function you call, this abstraction sends the same interface but different implementations of the same interface, so you are not aware of all of the exact classes, just the interfaces.\nHow: You create a class \u0026ldquo;called the factory class\u0026rdquo; and static methods. These static methods have to be verbose enough to tell you what kind of an object they are returning, and that\u0026rsquo;s how you build a factory class.\nCode Example For example, you have an application that runs on Windows and macOS. However, function calls for windows and macOS are different and you have created two classes to implement the click functionality. Based on the OS you are running your code on, you can decide what kind of button you want from the ButtonFactory class.\ninterface Button { click(): string; } class MacOS implements Button{ constructor(public arg1: string){} click(){ return \u0026#34;MacOS Button says \u0026#34; + this.arg1; } } class Windows implements Button{ constructor(public arg1: string){} click(){ return \u0026#34;Windows Button\u0026#34; + this.arg1; } } class ButtonFactory { private constructor(){} static getMacOsButton(arg1: string): Button{ return new MacOS(arg1); } static getWindowsButton(arg1: string): Button{ return new Windows(arg1); } } const MacButton = ButtonFactory.getMacOsButton(\u0026#34;Called from factory\u0026#34;) console.log(MacButton.click()) // MacOS Button says Called from factory const WinButton = ButtonFactory.getWindowsButton(\u0026#34;Called from factory\u0026#34;) console.log(WinButton.click()) // Windows ButtonCalled from factory Explanation Here there is a single interface called the Button and two classes MacOS and Windows implement that interface. Depending on what instance you want from the factory interface you can get it from the factory class. For Example const MacButton = ButtonFactory.getMacOsButton(\u0026quot;Called from factory\u0026quot;)\nAbstract Factory Design Pattern Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.\nReal-life example In India, a lot of restaurants have different types of cuisines and then they are divided by the region and then the type. The first layer of selection(the cuisine) provides the second layer of selection(the type of dish) and then comes to the dishes themselves. This is an example of the abstract factory design pattern. The Problem: Now in the above example of buttons, there was a separation based on OS. But imagine in a case of a framework, where there are buttons, alerts, input boxes, and so many things. The call can differ based on the OS they are on. Now imagine you as a developer instantiating a MacOS button, Alert and Windows button, Alert from the class itself; it would be tedious, wouldn\u0026rsquo;t it? To tackle this problem of\nProposal: Provide abstraction of creation of classes without exposing the classes themselves. Provide interfaces to provide factories then build objects through them.\nHow: Create a top factory class, and create methods to return sub-factory classes that return the final objects.\nCode Example Suppose you are building a web framework that runs natively on machines(macOS and Windows). The features that you want to provide in the different OS are the same however their implementation varies a lot. So you want to separate the instantiation of buttons, alerts, and other features based on the OS, then based on what the actual functionality is; giving the users choice of what kind of object they need.\nFor Example, You need to create an alert box for windows. The process would be Go through library =\u0026gt; Choose the functionality you want to do =\u0026gt; Choose the OS for the functionality, Here we are building abstract factories for this very particular use case.\nabstract class AlertBox { alert() {} } abstract class Button { click() {} } class MacOSAlert extends AlertBox { alert(): void { console.log(\u0026#34;MacOS Alert\u0026#34;); } } class WindowsAlert extends AlertBox { alert(): void { console.log(\u0026#34;Windows Alert\u0026#34;); } } class MacOSButton extends Button { click() { console.log(\u0026#34;MacOS Click\u0026#34;); } } class WindowsButton extends Button { click() { console.log(\u0026#34;Windows Click\u0026#34;); } } class ButtonFactory { Windows(): Button { return new WindowsButton(); } MacOs(): Button { return new MacOSButton(); } } class AlertFactory { Windows() { return new WindowsAlert(); } MacOs() { return new MacOSAlert(); } } class UIFactory { static Button(): ButtonFactory { return new ButtonFactory(); } static Alert(): AlertFactory { return new AlertFactory(); } } const MacOsButton = UIFactory.Alert().MacOs().alert(); // \u0026#34;MacOS Alert\u0026#34; Explanation Here we wanted to create an alert on the macOS, hence from the UIFactory, we called the AlertFactory which returned an instance of AlertFactory. It had two implementations of alert, one being for MacOS and the other being for Windows. We then called the macOS implementation.\nYou could also argue if the UI factory should be returning a MacOSFactory and WindowsFactory with their respective implementations, which is a correct implementation too.\nSingleton Design Pattern Singleton is a creational design pattern, which ensures that only one object of its kind exists and provides a single point of access to it for any other code.\nReal-life example Imagine a situation you and your friends order a really large sundae and eat it together. Throughout the lifecycle of that ice cream (until it melts or you eat it), it remains singular and you and your friends consume it. This single large sundae is a singleton. The Problem: In some scenarios you only want one instance of a class to be present, for example, you have a class that opens a session to the DB and you pass in query strings to it to receive objects. If this object were to be instantiated multiple times in a lifecycle of a program then there will be many connections open in the DB.\nProposal: You create an instance of the class and abstraction to get the instance of the class. That abstraction only returns a single instance of the class always.\nHow: You create a class with a private constructor, and you create a static method called getInstance or something similar. On invoking that function set a static variable of the class with the new instance and return the same instance every time that function is called.\nCode Example For example, you want to log events in a log file, You want to log a string when some function is called or when some event occurs. You create instances of logger at different places and log the events. All these events will be logged in a single file.\nclass LogFileWriter{ session: any static _instance: LogFileWriter private __dirname: string = \u0026#34;./logs\u0026#34; private filename: string = \u0026#34;log.txr\u0026#34; private constructor(){ session = open.file(__dirname, filename) } write(log: string){ writeFileSync(join(this.__dirname, this.filename), log, { flag: \u0026#39;w\u0026#39;, }); } getLogs(): string{ return readFileSync(join(this.__dirname, this.filename), \u0026#39;utf-8\u0026#39;); } static getLogger(): LogFileWriter{ if(!this._instance){ this._instance = new LogFileWriter(); } return this._instance; } } const Logger = LogFileWriter.getLogger(); const Logger2 = LogFileWriter.getLogger(); console.log(Logger == Logger2) // True Code Explanation The getLogger method on the first call creates the instance of the LogFileWriter class and sets it to the static variable. This on the other runs is then returned from the static class variable it is saved in.\nHowever, in a multithreaded environment, this can create multiple instances if a single thread has a different execution context than the current one.\nPrototype Design Pattern Prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes.\nReal-life example Ever been to a restaurant you do not go to and you see someone order something that looks delicious and you tell the server that you want the same thing? You don\u0026rsquo;t know what\u0026rsquo;s it details are, it just looks delicious to you and you ask the server to make the same dish for you. This is similar to a prototype design pattern.\nThe Problem: For example every time you create an instance of a class, there is a large blocking operation that takes place in the constructor of the class(Maybe a DB call or a large file read) that happens every time you create an instance, it\u0026rsquo;s better the next time you create the instance, you could somehow use the same data from the old instance. The prototype design pattern solves this problem by providing objects with a clone method, to copy their values to other objects.\nProposal: Provide an object cloning method to the class so that a .clone() method will return the copy of the object. I have here created a shallow and deep clone for shallow and deep copies.\nHow: Create an abstract class for the prototype design pattern, create a generic logic for cloning and extend all the classes with that abstract class where you want them to have cloning methods.\nCode Example abstract class IPrototype { Shallowclone(): any { return Object.assign(this); } DeepClone(): Object { return JSON.parse(JSON.stringify(this)); } } class DB extends IPrototype { constructor(public ARR: string[]) { super(); } } const d = new DB([\u0026#34;1\u0026#34;, \u0026#34;2\u0026#34;]); console.log(d); const d2 = d.Shallowclone(); const d3 = d.DeepClone(); console.log(d2); d.ARR[0] = \u0026#34;-1\u0026#34;; console.log(d); console.log(d2); console.log(d3); /** * * OutPut DB { ARR: [ \u0026#39;1\u0026#39;, \u0026#39;2\u0026#39; ] } DB { ARR: [ \u0026#39;1\u0026#39;, \u0026#39;2\u0026#39; ] } DB { ARR: [ \u0026#39;-1\u0026#39;, \u0026#39;2\u0026#39; ] } DB { ARR: [ \u0026#39;-1\u0026#39;, \u0026#39;2\u0026#39; ] } { ARR: [ \u0026#39;1\u0026#39;, \u0026#39;2\u0026#39; ] } * **/Code Explanation Here the base abstract class provides a DeepCopy and a ShallowCopy methods and the classes that extend them can use these methods to copy objects to other variables.\n","permalink":"https://madhavkhoslaa.github.io/posts/ceationaldesignpatterns/","summary":"Creational design patterns aid in the instantiation of objects by providing suitable abstractions and make the systems independent of how objects are created composed and represented.","title":"Creational Design Patterns: My lessons on it","topics":["Design Patterns","Gang of four","Singleton","Prototype","Factory","Builder","Uncle Bob"],"type":"article"},{"content":"SOLID is just an acronym for a few principles that Robert C. Martin compiled together. SOLID Design principles aim to make your code base extensible, easy to modify in the future, and easy to debug and fix. They are not rules just a bunch of thought ideas of segregation of logic and how to organize your code.\nS =\u0026gt; Single Responsibility Principle\nO =\u0026gt; Open Close Princple\nL =\u0026gt; Liskov Substitution Principle\nI =\u0026gt; Interface Segregation Principle\nD =\u0026gt; Dependency Injection\n1 Single Responsibility Principle According to the definition, SRP is:\nA class should have one and only one reason to change, meaning that a class should have only one job.\nBut, what does it mean ??\nIt means that if you have multiple functionalities in your application, then there should be a separate class or an entity for them, this leads to the separation of concerns, and fixing a bug related to a feature will be changed in the respective entity.\nFor Example, you have a program to write a diary and have options to store it to JSON, toS3, and whatnot.\nclass NoteBook { pageCount = 0; pages = []; constructor(public name) {} writePage(contents: string) { this.pages.push(contents); this.pageCount += 1; } getPage(idx: number) { return pages[idx]; } getPages() { return pageCount; } toString() { return pages.toString(); } toJSON() { return { contents: this.toString() }; } toS3() { aws.S3.uploadBucketData(this.toJSON()); } }1.1 What is the issue with this code? Let\u0026rsquo;s observe the behavior of the class in the above code.\nHandles the logic for notebooks. Such as adding pages, deleting pages, and getting page count. Parsing the code to JSON, String. Saving the Code to S3 or some file. If we observe functions like toS3(), there might be other classes that require the same functionality as well, such as a Diary Class? We might write the same code for the other class that might require the same functionality.\nDo you see the problem here? There is no code reusability a single class has too many functions and writing and persisting are tightly coupled to each other in this implementation.\nThis implementation over time will lead to the formation of a \u0026ldquo;God Class\u0026rdquo;, where a single class is doing too much work. This leads to difficulty in fixing bugs, specialized implementations for every single class, and not generic implementations.\nWe can fix these issues by thinking of different functionalities as different classes or different responsibilities, that handle only that set of related functionalities. Like a persistence class, a different class to create Notebook objects.\n1.2 A Better Implementation abstract class Persistance { toString() {} toJSON() {} toS3() {} } class NotebookPersistance extends Persistance { notebook: Notebook; constructor(_notebook: Notebook) { this.notebook = _notebook; } toS3() { // Some Logic to S3 } toJSON() { // Some Logic to JSON } toString() { // Some Logic to String } } class DiaryPersistance extends Persistance { diary: Diary; constructor(_diary_: Notebook) { this.diary = _diary; } toS3() { // Some Logic to S3 } toJSON() { // Some Logic to JSON } toString() { // Some Logic to String } } abstract class Writeable { pageCount: number = 0; pages: number = []; writePage(contents: string) { this.pages.push(contents); this.pageCount += 1; } getPage(idx: number) { return pages[idx]; } getPagesCount() { return pageCount; } deletePage(idx: number) { delete pages[idx]; } } class Notebook extends Writeable {} class Diary extends Writeable {} const RedNoteBook = new NoteBook(); RedNoteBook.writePage(\u0026#34;Java Notes\u0026#34;); RedNoteBook.writePage(\u0026#34;Python Notes\u0026#34;); const NoteBookSavingHandler = new NotebookPersistance(RedNoteBook); NoteBookSavingHandler.toS3();1.2.1 Observatations from the implementation above. There are two abstract classes one being Writeable and the other one being Persistence The Notebook and Diary classes extend the Writeable abstract class The NotebookPersistance and DiaryPersistance classes extend the Persistance abstract class Here the writing implementation and the saving of the writing implementations are loosely coupled and not in a single class coupled together. 1.2.2 How is this implementation better than the one above? Here the two responsibilities of creation, the writing of a notebook and the storage of it are loosely coupled and are written separately.\nLessons Thinking of responsibilities and features similar to each other to be in a similar object that can apply the desired functionality to any other object that requires it.\n2 Open Close Principle According to the definition, OCP is\nSoftware entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”\nThe idea is to write your code in such a way that to add the functionality you do not have to fiddle with the existing code. The idea sounds great but when one of your classes has to change and you have to reflect on changes in all other classes. For example, a method from class A returns a value x and after the change, it returns an instance of a class y. You have to change your logic every place where that function is called.\nSo how do you solve this problem of a changing class being a dependency?\nBertrand Mayer proposes to use inheritance to achieve this goal since, in his words\nA class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as a parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.\nlet us see this principle in an example.\nSuppose you have an API where you and multiple engineers are working together in different teams. And the product calculates the area of polygons. You have a class for squares in your codebase already, you get an immediate requirement where you have to add support for rectangles, what do you do? Overload Area and Perimeter in the square class itself? Or do you extend a class for polygons and create new implementations of rectangle logic?\nThe latter option, since it brings no changes to the place where square logic is used, it is logically separated from the square class and you did not have to change the existing codebase.\nThis is a vague code for the situation.\nabstract Class Polygon{ Area(){} Perimeter{} } Class Square extends Polygon{ Area(len: number){ return len*len; } Perimeter(int len){ return 4*len } } Class Rectangle extends Polygon{ Area(len: number, br: number){ return len*br; } Perimeter(len: number, br: number){ return 2*(len + br); } } Class Triangle extends Polygon{ Area(len: number, br: number, height: number){ return 0.5 * len * br* height } Perimeter(len: number, br: number, height: number){ return len + br + height }}Lessons When you have a large code base or code bases. Where a single class is being used in different places, or the java ecosystem a single jar file is being used in multiple code bases. You need to take account of multiple devs working on the same code base. Your changes should be such that they are not changing the existing implementation of the code, rather than just building on top of it. Unless(obviously) you\u0026rsquo;re fixing a bug in a function.\n3 Liskov Substitution Principle Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.\nin other words, \u0026ldquo;If S is a subtype of T, then objects of type T may be replaced with objects of type S\u0026rdquo;.\ninterface Family { Message(){} LastName() FirstName() LastName() } class Parent implements Family{ constructor(){} Message(){ return \u0026#34;I\u0026#39;m a parent\u0026#34;; } LastName() FirstName() LastName() } class Child extends Parent implements Family { constructor(){} Message(){ return \u0026#34;I\u0026#39;m a child\u0026#34;; } } class FamilyLogger { f: Family constructor(f: Family){ this.f = f; } log(){ console.log(Object.getOwnPropertyNames(Math).filter(function (p) { if(typeof Math[p] === \u0026#39;function\u0026#39;){ console.log(p()); } })); } } const p : Parent = new Parent(); const c : Child = new Child(); const Parentlogger = new FamilyLogger(p); const Childlogger = new FamilyLogger(c); Parentlogger.log(); Childlogger.log();Here we have a logger class that takes in an object of type family in it. The Parent class implements the family interface and the Child class extends the Parent class.\nThe logger class iterates through each function in the local instance and runs it and logs to the console.\nHence we see that both parent and child instances are passed to the same class without breaking the code.\nThis is essentially what LSP is.\n4 Interface Segregation Principle Clients should not be forced to depend upon interfaces that they do not use.”\nThis principle is sort of an extension of the first solid principle with the central theme being interfaces.\nA single interface should not have multiple responsibilities forming a god interface instead there should be multiple interfaces for different responsibilities.\nThe interface should exist in such a way that classes that implement those do not have many unused functions.\nAn interface should aptly represent a class that it implements, it should not hide major implementations nor share unused implementations.\nExample: Assume we have an RPG game and certain characters do similar activities. Characters being an ork and a wolf. These characters can Attack, Walk, Breathe and do a few activities that they do that are not common. Like an ork can lift a boulder and a wolf can run fast.\nLet us try naively creating an interface for them.\ninterface Charecter{ Attack() Walk() Breathe() LiftBoulder() RunFast() } class GreenFatOrk implements Charecter{ Attack(){ return \u0026#34;Ork Attack\u0026#34; } Walk(){ return \u0026#34;Ork Walk\u0026#34; } Breathe(){ return \u0026#34;Ork Gasps\u0026#34; } LiftBoulder(){ return \u0026#34;Ork lifts boulder\u0026#34; } RunFast(){ throw new Error(\u0026#34;Method not implemented\u0026#34;) } } class GreyWolf implements Charecter{ Attack(){ return \u0026#34;Wolf Attack\u0026#34; } Walk(){ return \u0026#34;Wolf Walk\u0026#34; } Breathe(){ return \u0026#34;Wolf Gasps\u0026#34; } LiftBoulder(){ throw new Error(\u0026#34;Method not implemented\u0026#34;) } RunFast(){ return \u0026#34;Wolf runs so fast\u0026#34; } }4.1 What is wrong with this code? Here we can see we have a big fat interface that both \u0026lsquo;ork\u0026rsquo; and the \u0026lsquo;wolf\u0026rsquo; class implement. We can also observe functions that wolf does and ork does not. In both of these implementations, we are throwing errors that are not implemented. This does not seem since we have functions in a class that doesn\u0026rsquo;t do anything and it can be misleading to other engineers who are just using the classes, this also creates ambiguity and confusion.\n4.1.1 A Better Implementation interface Charecter{ Attack() Walk() Breathe() } interface ork extends Charecter{ LiftBoulder() } interface wolf extends Charecter{ RunFast() } class GreenFatOrk implements ork{ Attack(){ return \u0026#34;Ork Attack\u0026#34; } Walk(){ return \u0026#34;Ork Walk\u0026#34; } Breathe(){ return \u0026#34;Ork Gasps\u0026#34; } LiftBoulder(){ return \u0026#34;Ork lifts boulder\u0026#34; } } class GreyWolf implements wolf{ Attack(){ return \u0026#34;Wolf Attack\u0026#34; } Walk(){ return \u0026#34;Wolf Walk\u0026#34; } Breathe(){ return \u0026#34;Wolf Gasps\u0026#34; } RunFast(){ return \u0026#34;Wolf runs so fast\u0026#34; } }4.2 Observations about the better implementation Just from the first glance, we see that both the implementations of the GreyWolf class and theGreenFatOrk class have no functions that are not implemented. The common functions are defined in a separate interface and the specific interface is defined in the respective wolf and ork interface.\nLessons You should separate your interfaces such that there is no overlap in the interface functions between two different implementations, such that some functions are not implemented. Specialized interfaces should extend a generic interface to form a specific implementation. This leads to a clear boundary between why the two classes are different.\n5 Dependency Inversion high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.\nWhat it means is that class A uses an instance of a DB session. That DB session is defined within the constructor of the class. This session is only connected with the production DB.\nWhat if you have a scenario where you need to test that class using a test DB session? What will you do? How will you solve this?\nDependency Inversion aims to solve this by saying that dependencies should be given to the class where it is called from, the place where the class is instantiated is responsible for passing in the instance of DB to the class, hence inverting the control of dependencies.\nclass StudentDao{ constructor(){ this.session = session.connect(\u0026#34;mongo://example.url\u0026#34;) } getStudentbyName(name: string){ this.session.findOne({name}) } getStudentbyRoll(roll: number){ this.session.findOne({roll}) } } // In Application Code const studentdio = new StudentDao() studentdio.getStudentbyName(\u0026#34;Bob\u0026#34;) // 5.1 What is the issue with the code? The StudentDao class is tightly coupled with the production session. If we want to use the class in our test environment, this would be hard since we want to run the class on a DB session instance which is not present. Hence we can pass in the instance of the Database session where we instantiate the class.\nclass StudentDao{ session: SessionType constructor(session: SessionType){ this.session = session } getStudentbyName(name: string){ this.session.findOne({name}) } getStudentbyRoll(roll: number){ this.session.findOne({roll}) } } if(process.ENV.name === \u0026#34;test\u0026#34;){ const studentdio = new StudentDao(new TestMongoSession()) studentdio.getStudentbyName(\u0026#34;Bob\u0026#34;) } if(process.ENV.name === \u0026#34;prod\u0026#34;){ const studentdio = new StudentDao(new ProdMongoSession()) studentdio.getStudentbyName(\u0026#34;Bob\u0026#34;) }Lessons DI is just an implementation of Inversion of Control (External link). It is when you let outside of the class choose what generic implementation should be used in an instance. This makes it easy to create pluggable implementations. It also helps in testing where you want a certain class to use a certain instance while testing and a different one while being run in production.\n","permalink":"https://madhavkhoslaa.github.io/archive/solid/","summary":"SOLID Patterns are a general guideline and recommended patterns that you can use in your codebase to be extensible, easy to modify in the future, and easy to fix. These aren\u0026rsquo;t hard-bound rules just general recommendations. Here, I\u0026rsquo;ll talk about them in detail.","title":"SOLID Patterns: My lessons on it","topics":["SOLID","Design Patterns","Uncle Bob"],"type":"article"},{"content":"Caching is a cheap way to scale a system\u0026rsquo;s performance in very less time. Being cheap and easy to implement on the top of a existing system it also brings some problems such as invalidation and infrastructure for setting up the cache itself. Here I plan to talk about some lessons on caching that I\u0026rsquo;ve learnt in a few months.\nPhilosophy of Caching The supreme art of war is to subdue the enemy without fighting it - Sun Tzu\nCaching Caching in a pure function system is when you store the arguments and the result to a particular function and use the result stored instead of actually invoking the function this comes with a prerequisite that the function for those arguments is already invoked and stored in the cache. For a good approach to caching you might want to design your caching scheme in such a way that for most responses the cache is actually used instead of performing a expensive computation again and delaying the response from your service. This need of reusability of cache from cached objects is called increasing the \u0026ldquo;Cache-Hit Ratio\u0026rdquo;. We will talk about \u0026ldquo;Cache-Hit Ratio\u0026rdquo; now and ways to maximise it.\nCache-Hit Ratio Mathematically it is the ratio of cache used to total objects cached in your system; But what does it convey to you ? A high cache hit ratio means that your cache was used for most objects that were cached and hence you saved alot of CPU time for logic that was already computed. So how do you achieve a high Cache-Hit Ratio ?\nMethodlogies for increasing the \u0026ldquo;Cache-Hit Ratio\u0026rdquo; Cache Key Space - Describes the number of keys in your cache if you have a key value based cache sysem. Average TTL - How long a particular object is cached for, you just don\u0026rsquo;t want to store objects forever since storage costs $$, Longer the TTL the better, You do not have to worry alot since algoritms like LRU and so on manage this for you. Average size of cached object - This affects the first object in this list due to hardware constricts you can obviously store a certain amount of objects depending upon their size. The larger the size of the object the lesser the Cache Key Space it, Hence it might lead to a lower Cache-Hit Ratio How to achieve a large Cache Key Space ? Choosing a scheme in which cache is common among users Keeping the size of the cached object such that more objects could be stored and hardware is not a construct. Cache rule of thumb Cache high up the call stack Reuse cache among users Where to cache (Cache high up the call stack)? In this scenario imagine you have 3 functions calling each other in the call stack and the result is produced by calling them all. Assume you call f(a) and inside the implementation it passes the value to f(f(a)) and so on. I am assuming the cost of each operation likewise in the table.\nFunction Time f(a) 15 ms f(f(a)) 10 ms f(f(f(a))) 5 ms Time to fetch cache 1 ms Let\u0026rsquo;s assume the following scenarios for caching the call stack.\nFunction Cached Time Time(ms) No Cache f(a) + f(f(a)) + f(f(f(a))) 30 ms f(f(a)) f(a) + f(f(a)) + Cache Time 26 ms f(f(f(a))) f(a) + Cache Time 16ms f(a) Cache Time 1ms Observation : The higher we cache up the call stack the less functions are called and the time for resolving the function is reduced.\nHence, as a rule of thumb caching high up the call stack is the best approch for caching in a system where function calls are chained and are a bottleneck in terms of time.\nWhen to expire cache ? Scenario 1: Assume you have a User A and you have a system where you have multiple inputs such as User A\u0026rsquo;s Height, User A\u0026rsquo;s Age, User A\u0026rsquo;s Major and you have an heuristic algorithm where you determine what operating system the user uses. And also look at a key-value cache as an example running sideways\nImagine two scenarios in this case.\nat T0\nInputs Time Age 22 Height 175 cms Major CS Algorithm predicts that User A uses Arch Linux.\nCache looks like this at the moment.\nUserId OS UserA ArchLinux at T1\nUser A changes his major to design and now for some reason uses MacOS.\nInputs Time Age 22 Height 175 cms Major Design If the cache looks like T0 then we will get a stale value for the OS of the user.\nUserId OS UserA ArchLinux Algorithm predicts that User A uses MacOS but according the the cache it\u0026rsquo;s Arch.\nNow, we know that the user will be using MacOS but the cache will return Arch Linux. to the front end.\nSo, we take a lesson that cache is always a side effect of the inputs that you put in a system, if you change the inputs you have to update the cache to the most recent result.\nYou need to make sure to detect any inputs that the cache is a side effect of and if they change there is a need update the cache value aswell. After the updation the cache looks like this which is a accurate and consistent representation of the application result.\nUserId OS UserA MacOS Scenario 2: Assume you make a cool and hyped application and there are alot of new users logging in. You are caching some value for every user that get\u0026rsquo;s calculated of user inputs. You have a large cache key. But for some reason the older users are not logging in anymore and the hype is dead. You are still storing cache of the old users and the current users that are still using the platform. Caching on hardware is expensive and not utilised cache means still bills from cloud providers.\nSolution: You delete the cache for the users that are inactive and are not using the platform anymore; Thankfully for this kind of invalidation you do not have to worry. Most caching technologies for example: redis support LRU iviction for deleting unused caches and keeping your cache hit ratio high.\nTypes of caching technologies Browser Cache - The first and the most common type of caching layer built in the modern browsers is the browser cache. Browser caches have ability to not send requests if the data is already cached. This can be seen in the request times even on sdplus.io site loading.\nCaching Proxies - By an ISP. It is a read through cache that could cache traffic. THe larger the network the larger the potential cache hits. Now they don\u0026rsquo;t happen because of SSL the proxies do not have permissions to decrypt the payload and also bandwidth is much cheaper.\nReverse Proxies - This works like caching proxies only and is a read through cache, instead it is installed at the datacenter side (your server). They are an excellent way to scale and to speed up the service layer.\nCDN (Content Delivery Network) - A group of distributed servers based of geolocation hosting content to users in different parts of globe.\nCaching for different use cases Read Intensive - When you have a application where the front end requires alot of data to be displayed from the multiple parameters. Write Intensive - When you have an application where a large amounts of inputs are taken, computed and stored in the DB. (Something like a MBTI test ? Where you put multiple inputs and get a MBTI value.) Read Trough and write through cache Read through cache The use case if for a read intensive application Write through cache The use case if for a write intensive application ","permalink":"https://madhavkhoslaa.github.io/posts/post1/","summary":"Caching is a cheap way to scale a system\u0026rsquo;s performance in very less time. Being cheap and easy it also brings some problems such as invalidation and infrastructure for setting up the cache itself. Here I plan to talk about some lessons on caching that I\u0026rsquo;ve learnt in a few months.","title":"Caching: My lessons on it","topics":["Caching","Redis","Memcached"],"type":"article"},{"content":"A RESTful API for small e-commerce businesses, built with Node.js, Express and MongoDB (Mongoose). 20 stars, 10 forks.\nSource: github.com/madhavkhoslaa/E-Commerce-API (External link)\n","permalink":"https://madhavkhoslaa.github.io/projects/ecommerce-api/","summary":"A RESTful e-commerce API in Node.js, Express and MongoDB.","title":"E-Commerce API","topics":[],"type":"project"},{"content":"U-Net semantic segmentation in PyTorch. It has a custom N-class one-hot encoder and supports aerial image datasets. 12 stars, 4 forks.\nSource: github.com/madhavkhoslaa/Pytorch-U-Net-Segmentation (External link)\n","permalink":"https://madhavkhoslaa.github.io/projects/unet-segmentation/","summary":"U-Net semantic segmentation in PyTorch for aerial imagery.","title":"PyTorch U-Net Segmentation","topics":[],"type":"project"},{"content":"Hey, I\u0026rsquo;m Madhav. I\u0026rsquo;m a systems engineer working close to the metal: the Linux kernel, eBPF and the networking stack.\nNow Since August 2026 I\u0026rsquo;ve been freelancing and working through the Linux mm, net and eBPF subsystems in depth.\nUpstream kernel. My first patch is merged into bpf-next: a selftests/bpf fix where csum_partial() dropped the trailing byte on odd-length buffers. More patches are in progress. Kukri (External link). A Linux network ACL/firewall in Rust and eBPF, with XDP on ingress and TC on egress. MAC, IPv4/CIDR and TCP/UDP port filters are chained with BPF tail calls, so a disabled rule category costs nothing. A libbpf-rs control plane keeps maps in sync, and a ratatui TUI edits rules live. ARP from scratch (External link). ARP over raw AF_PACKET sockets in C and Rust, with no networking libraries. I wrote about it here. Before I spent five years building backend systems, mostly in payments and fintech. That work is why I care about systems today.\nTrackk (2026). I built a Rust market-data service that takes NSE UDP feeds for 13K+ instruments and turns them into candles. It pushes them to clients over a multithreaded TCP server in under 100ms end to end. I also built a Rust order management system with SL/SL-M triggers, and BSE IPO bidding over UPI. HighLevel (2024–2026). I owned the WhatsApp workflow infrastructure on Google Pub/Sub: up to 50M events a month and 1.7M messages a day. A lot of that job was fixing distributed race conditions, such as duplicate sends, lost updates and out-of-order processing. BharatPe (2022–2024). I worked on UPI payments: 55K+ transactions a day at sub-200ms latency. I integrated Unity Bank as a VPA handler. I also partitioned a 1B+ row MySQL transaction table, which cut queries from 15s to 200ms. SDPlus.io (2021–2022). I designed Trickle, an internal backend framework, and moved core APIs to GraphQL. Why systems I\u0026rsquo;ve always focused on observability. Staring at traces between services, DB logs and throughput-versus-latency graphs, I kept wanting to know what happens one layer down. So now I\u0026rsquo;m going down there: syscalls, sockets, packets, and the kernel code that moves them.\nThis site is where I log what I learn.\nElsewhere Code: github.com/madhavkhoslaa (External link) Work: linkedin.com/in/madhavkhoslaa (External link) Posts: x.com/futexmaxxing (External link) Thanks ~ Madhav :)\n","permalink":"https://madhavkhoslaa.github.io/about/","summary":"Systems engineer working on the Linux kernel, eBPF and networking. Previously backend at Trackk, HighLevel and BharatPe.","title":"About","topics":[],"type":"article"}]