Exercise 2: A Request Larger Than All of Memory Fails Cleanly — Possible Solution ==================================================================== THE TEST ------------------------------ mem = PhysicalMemory(500) too_big = mem.alloc_first_fit(501) print(too_big) still_fine = mem.alloc_first_fit(500) print(still_fine) RESULT ------------------------------ requesting 501 bytes from a 500-byte memory returns: None requesting exactly 500 bytes (the whole memory) returns: 0 The impossible request returns None -- no exception, no crash, no nonsensical negative or out-of-range address. The exact-size request, run afterward against the still-untouched 500-byte memory, succeeds and returns address 0. WHY THE IMPOSSIBLE REQUEST FAILS CLEANLY ------------------------------ alloc_first_fit()'s own loop checks every entry in the free list -- initially just [(0, 500)] -- against the condition block_size >= size. For a request of 501, 500 >= 501 is False for the only entry that exists, so the loop finishes without ever finding a usable block and falls through to the explicit return None at the end of the function. There's no array-indexing, no arithmetic on addresses that don't exist -- the function was written to have an honest "I couldn't do it" outcome built in from the start, not just an accident of what happens to survive. WHY THE EXACT-SIZE REQUEST STILL SUCCEEDS ------------------------------ 500 >= 500 is True, so the single free block is used in full: since block_size == size exactly, the code path is del self.free_list[i] rather than shrinking the entry -- the entire free list becomes empty, and address 0 (the start of that one block) is returned. This confirms the allocator doesn't reserve any hidden slack or require free space to remain after a successful allocation; using every last byte of a memory is a legitimate, fully supported outcome. WHY BOTH HALVES OF THIS EXERCISE MATTER TOGETHER ------------------------------ Testing only the failing case could leave open the question of whether the allocator is simply broken near its own capacity limit -- maybe it fails a request for 500 too, or crashes right at the boundary. Testing the boundary from both sides (501 fails, 500 succeeds) confirms the limit is exactly where it should be, with no off-by-one error in either direction. WHY THIS WORKS AS AN ANSWER ------------------------------ Confirming a clearly-impossible request degrades gracefully to a plain None return, and that the true capacity boundary is exactly where the memory's own declared size says it should be, verifies the allocator behaves predictably and safely right at its own limits -- exactly the condition where a poorly-written allocator is most likely to misbehave.