 Hey guys, welcome back. Skitsown episode 11 topic today is going to be heaps and everything that comes with that including allocation and freeing of Memory chunks as well as linked lists So let's just get into it. So question of the day. What's a heap? Honestly, not really sure something something memory Say something allocation Not really all that sure. Basically, we're gonna implement two functions here alloc and free that basically Turn a big huge chunk of memory into something useful so basically the alloc function will return a the address of some chunk of memory that is Designated for some purpose. So let's say you requested eight squares of memory in this chunk Whatever that means. Well, then this alloc function would point you towards the first byte the address of the first byte in that chunk and then the free function would just take an allocated chunk say at least eight squares and Turn that into free space that could then be used for a future allocation so kind of vague definition there basically a heap is just a Region of memory that we're going to develop an algorithm for that we can allocate in free regions of that heap for different purposes So what is the use case for something like that? Honestly, nothing. Let's let's be real it's for very Indecisive people like women for example So it would be used whenever you would need some kind of variable amount of memory at runtime That you weren't prepared for necessarily To define that amount of memory At compile time. So here's a few examples of what you might use a heap for Let's say you wanted to keep some kind of Let's say you had some kind of iterative process where you had an unknown number of iterations Some kind of algorithm that you didn't know beforehand how many iterations it would take to complete But you wanted to keep records at each iteration of some quantity of interest in this case I'm talking about convergence histories for optimization, but it could be anything Now if you wanted to save data from an you know unknown number of iterations You'd have to either a have an array that was big enough you'd have to have an you know a Million element array and guaranteed that you weren't gonna have a million iterations, which is fine or you can just choose to allocate a chunk of memory per iteration and then You know link them together and that would allow you to have exactly the amount of memory you need For the number of iterations that you end up having that's one possible use case another might be Let's say you had some kind of an CAD software that you were writing with Lists of geometric elements like faces and nodes edges Solids, etc. And you wanted to just add an edge to the list of edges. Well It's not so easy if you had an array of Edges to add another element if you had 20 edges You wanted to add a 21st edge You might have to make a whole new array and copy all the elements over Plus the new element at the bottom and so that might not be sufficient now a linked list would be much a much better way in my opinion to keep track of Lists of things that are constantly changing So that would be another use case for a heap and just the mimic memory in general and a last use case might be Let's say you had some kind of linear algebra algorithm where you had some intermediate steps where you wanted to Maybe allocate some memory do some operation and then immediately throw away the intermediate value Well, the a heap would be a great way to get that perfectly sized chunk of memory Just for you know that moment when you need it So yeah, there are a couple use cases for heaps all those things can be accomplished without a heap You can do all this things in just arrays, but sometimes it's nice to be able to Get memory at runtime as you need it Okay now Question that we are always asking ourselves in this series. Can we implement this from scratch? Obviously the answer is yes, and The boomers might say well, you know, there are syscalls that you can use for this purpose No memory map memory unmapped SBRK and other and others And you can use those and they're probably better than what we're going to be doing because we're going to be implementing our, you know Stuff from scratch and we're going to be defining a chunk of heap space act basically when the program is Loaded into memory and so it's not really a true heap, but it does have a lot of the same underlying functions And so our you know low IQ implementation is going to be pretty pretty simple and this is kind of how it looks basically the heap starts off as a single chunk and each chunk well, I guess it's Initially one chunk, but it will eventually become a sequence of chunks and each chunk has this form And so you have an 8-byte header You have an 8-byte footer and then in between you have a Body which is the space that you're going to use for your you know use case And that's going to be a multiple of 8 bytes in length And so the header and footer simply encode the length of the body and Then the low bit because that's always going to be multiple of 8 you have a couple of extra bits You can use for other information. I'm just using you know just some you know very trivial Stuff here, so if it happens to be plus zero, so let's say your your header was you know 16 well that means that length of your body is 16 bytes and it is not allocated so far, but if your header was 17 That would mean that though your body length is 16 bytes and it's allocated Right, and in this last one you don't really need this, but I decided it was a good idea to say if your if your footer was a Multiple of 8 plus 2 that meant that you were the final footer of the entire heap You don't need that. I just chose to put that in but you can work around that if you don't want to As I said as I said before the heap is one big chunk initially But again, you're breaking that up as you need smaller chunks for your You know usage okay, I think the best way to go through this is with an example and So our example here is basically you have a heap of 128 bytes each of these rectangles is one bite in this kind of grid And we're going to be allocating, you know one by one two different chunks of 16 bytes And so here you can see kind of how the heap looks in the beginning. So you have 128 squares I believe one two three four five six seven eight one two three four five six seven eight. Yeah And so we've dedicated eight bites at the beginning and at the end For header and footer meaning of the 128 bytes heap 16 bytes are thrown away. And so actually the header only points only indicates that our you know Chunk is only 112 bytes because we've lost 16 bytes from the header and footer combined And then obviously the header is 112 but the footer is 112 plus 2 because I said before Plus 2 if you're the final footer again, you don't need that but I'm gonna have that in this series Okay, so that's how the heap is at the beginning before we've done anything to it now We're trying to allocate two different chunks of 16 bytes. And so after the first chunks allocated, this is how the heap looks and so We basically, you know grabbed our first chunk and we've ripped off 16 bytes plus 8 bytes for the header and footer. And so before we had 112 bytes of free chunk now our free chunk is just 80 bytes and you can see here we have a Header which is 16 plus 1 and a footer which is 16 plus 1 Indicating that after our first chunks allocated, we know we've lost 32 bytes of a free space in our Heap and that's why our heap used to be 112 Bites of free space and now it's only 80 bytes of free space Now if you went to the next chunk allocation again, you've ripped off another 32 bytes And in a similar way and so your heap which before had 80 bytes of free space from here to here Now only has 48 bytes from here to here So that's kind of giving you an idea visually of how our heaps can be set up with you know, different Seek a sequence of different chunks every chunk having a header and a footer and Now we can talk about the algorithm. So it's a very very trivial algorithm. I almost feel bad going through this but Basically you start at the start of the heap the heap start address and if you want to allocate n bytes you basically go chunk by chunk through your heap looking for an unallocated chunk meaning a chunk that Has a Header and a footer with a little bit of zero. I mean and basically an even value Until you can find a Chunk that's unallocated of at least n bytes Obviously if you're looking for 16 bytes and you find the chunk with 8 bytes of free space, it's not going to be enough so then what you do is you With the chunk that you found assuming that's big enough and it's not allocated You check well, how big was that chunk and if that chunk is Super large like for example in this case where the chunks were you know, the free chunks were well larger than they had to be You have to break down that chunk in the smaller pieces because it would be a waste if you wanted to allocate a single chunk of 16 bytes And you use this entire heap for that purpose, you know, you're wasting 80 You know 80 bytes of space basically so that would be a complete waste And so basically if your chunk has more than n plus 16 bytes You want to break that into two chunks if the chunk is less than that There's not enough room for another header and footer and so just use the whole chunk Let's say for example instead of you know, wanting to allocate a hundred or 16 bytes Let's say you want to allocate, you know a hundred bytes There wouldn't be enough space after allocating a hundred bytes for anything else and so yeah, you know That's just to not waste the not waste too much space And once you've done that you just set the header and footer accordingly based off the amount of bytes that you're going to be allocating in that chunk And then you return the address of the first byte. So in this case you'd return the That's the after you've allocated the first chunk of 16 bytes you return this address, which is the first byte of the Allocated zone not counting the header and the footer and this way in your algorithm Whatever you happen to be doing you're able to drop 16 bytes of data in All the bytes after the byte that you've indicated is the start byte for your chunk Okay, a lot of words not very much meaning the Other algorithm besides allocation would be for freeing and freeing is even easier The only thing is you have to be able to consolidate when you have multiple free chunks Together what I mean by that is let's say you had this particular heap you know construction where you had two Allocated chunks of 16 bytes and then a free chunk. Let's say you wanted to free, you know This memory here Well, it's not simply a matter of taking the plus one and making it a plus zero I mean that would work But it wouldn't be truly what you wanted to do what you would actually want to do is you do want to combine this region with this region Basically control zing this entire process and bring you back to this and so you have to be able to not just Free an allocated chunk, but also be able to combine adjacent free chunks as well If that makes sense And that's what this algorithm is doing here. It basically it's just saying look at the previous chunk Look at the next chunk if either of them is free Well, then just combine them all into one big free chunk. I won't get into the details here. You can read the text Last thing I want to talk about is what is a linked list? So a link list pretty pretty straightforward It's just a list of things that has some kind of a link between them Here's a very straightforward example of a linked list Basically, you have some object at some address that you've again allocated this dynamically on the heap for example or maybe not and In this object, you have, you know, some quantity that you you know want to keep track of but also you have Indication of the next object in the list So in this case, I have the address of the next object object to and that object again has a number and it points to another object and So the idea is basically every element just simply has a pointer to the next element or the previous element or just some other element or Perhaps multiple other elements and that would be you know a linked list So there's uses for this it makes it easy to insert things into a list It's easy to reorder things in a list because you're just working with addresses and they're not working with actual arrays. So Simple stuff. We'll talk about an example of that in the future. So Get into that in a few minutes Now the code so I've got four different examples here one So our example a is just a basic usage of our heap example B is an extension of that with multiple alex and freeze then I have a very simple linked list implementation and lastly, we have a Way to check for leaks. I know that memory leaks are always a big concern for people Not me, but if you happen to care about that, that's a very simple way to check for leaks if you use this kind of a Heap paradigm, I guess you'd call it okay into the code So Here are the examples you can get them on the soy hubs depository. Let me first show you all of the heap related functions that we have in our library and So the first Function I have I want to show you is our heap init function And this is function that we have to call every single time We use a heap at the beginning of our code and all this does is it basically It sets up the heap basically in the way that I had just shown you It just basically drops the header and the footer in to a trunk of memory. So in other words, we're basically That function basically creates a heap that looks like this Given a heap size of some number and a heap start address of some location It generates this kind of a header and footer You know as you see here, okay, so that's that next function I have is Heap Alec and this again just talks this just does what I talked about before it allocates a chunk of memory Searching chunk by chunk for a chunk that is big enough and it's not already allocated and then it Conditionally breaks that chunk up if it's big enough or not you can look through the code if you'd like Heap free does the heap free out with my talk it up before again. It just looks for a chunk and then checks the adjacent chunks and it frees all of them as necessary and then The last function I have here is heap eval. This just returns the number of Allocated bytes and number of allocated chunks in the heap so you can kind of keep track of memory leaks So simple stuff take a look if you're curious And now into the examples. So our first example was example a and let's take a look at the code here before I run it and so here is an important thing so it before you know, we had our print buffer to you know print things out to the screen and this few syscalls as possible and We had a print buffer size for that now. I'm also adding a heap size. So we have another Chunk of memory that the perk that the OS will load when the program loads and So in our program header, I've added heap size to the size of our segment when it's loaded into memory So now our program When it's loaded is now even bigger than the binary by this value heap size and Then the heap location is actually at the very bottom of our code here You can see after the print buffer I have again this could be anywhere It doesn't have to be defined here, but just for you know simplicity say I put at the bottom I say heap start address equals the print buffer plus the print buffer size. So that means When the program is loaded into memory this Quantity heap start address, you know is is defined to be immediately after the print buffer So that means our heap init function, which we call here The function that drops the header and footer into place that I described before that drops the header and footer basically after the print buffer in our 128 byte chunk That makes sense. Okay So here we're using one of our debug functions Which is called print memory to just print the heap out before we do anything and then here You can see I'm calling heap allocate to allocate in this case 64 bytes of memory And then I can check whether or not that succeeded with a simple test instruction And then if it failed I can print out something if it didn't fail I can continue on with the program Here you can see I have the memset function which just sets that entire chunk to sevens Then I print the heap out again. Just to show that then we can try to free the chunk So, let me show you how that looks when we run it Let me actually pipe that into Last so you can see that And so this top little bit here You can see my cursor This is the heap before we do anything. So it says 0x70. That's the header. So these eight bytes. That's the Header of our heap our single chunk free heap And that I believe that's 128 minus 16 so that would be 112 and then our footer is 72 because again, it's Just 70 plus 2 2 being the last footer in our heap Then you can see here. We're trying to add or sorry allocate 64 bytes. And so you can see here that Did happen the allocation the allocation function works and we've dropped 64 bytes in this case of all sevens at Address 21660. So 21660 is the appointed first address of that Allocated chunk and you can see here the header and footer are now 0x41 for that chunk again That's the 64 plus one one meaning that it's allocated zero being that it was free And then you can see because there was extra space at the bottom of that chunk There's still 0x20 bytes hex of free space on our heap Then you can see here We've tried to free that space with the heap free function So what that did was it basically it didn't change the memory the memory is still there the sevens are still there but you can see the the header now Suggests that the heap is back to what it was before with 0x70 hex bytes or free space But you can still see here evidence of the previous heap 0x41 and 0x20 those were the old footer and header of the Internal chunks which again now I didn't reset them to zero, but they're just no longer Important quantities Okay, cool The next example I wanted to show you was having basically multiple alux and freeze Let's take a look at the code here To show you what's going on So again, we initialize the heap. I think it's the same size heap as before in this case our first thing that we do is to be allocate 24 bytes of 0x1 And we save that address in a register in this case are 13 Then we allocate 24 bytes of 2 and 24 bytes of 3s Then we are printing the heap out Then we're freeing the first and the third chunk And we're pretty to keep out again Then we are allocating 32 bytes in this case of 4s and then 8 bytes of 5s And then we are printing it out and then we're freeing all the chunks and printing it out finally again Let's see how that looks when we run it Put that into less again So you can see here the first thing we did was we reserved three chunks of 24 bytes and so you can see here you know 0x 19 that's 24 bytes plus 1 indicating that it's allocated and we've allocated 24 bytes as you can see of 1s and Then of 2s and then of 3s Then you can see here. We're trying to free two of those chunks and so you can see here The 0x1 and 0x3 chunks now no longer have headers and flitters that are odd now. They're back to being even right so it used to be 19 for the first chunk 19 for the second chunk and 21 Or 22 for the third chunk at this point the first and third chunks are now 18 and 20 so that worked now the first and third chunks are freed and now we're trying to reserve chunks of 32 bytes and 8 bytes and so you can see here The first chunk that was available inside, which we had, you know, 32 bytes of space was the bottom chunk And so that's where the allocation algorithm dropped our 4 bytes now 0x4 and then At the top we had enough space to fit in our 0x5s and so that's where that was dropped And then lastly here you can see when we're done Freeing all the chunks were back to the original header and footer of 0x70 and 0x72 All the internal values have not been tampered with there's still you know what they what they were before Only the head of the footer have been adjusted to indicate that now this whole heap is one big free chunk. Okay Let's go into our linked list example now So let's check out the code So in this case Our linked list elements are basically like this so each element has a pointer to the next elements address Followed by a value each of those is an 8-byte, you know quad-word and So this function here is kind of our add element function that you might Know used to add elements to your linked list So I won't go through the details on that you can take a look at the code go in the line by line and see how it works basically it just sets the Quantities in this element structure accordingly So yeah, and in this case all we're doing is we are Initializing our heap creating our first element with a certain value in this case the 0x1111111 and Then we're adding linked list elements for a 0x222 0x333 and 0x444 Printing it out and then ending the program. So let's run that and you can see here the Four different linked list elements that we have so the first one is at this of this address second one this address or one address fourth one this address and you can see that At each of those locations, you know the header and the footer are 0x11 0x111111111111 and then one two because it's the final footer in our heap But then also in each element the second entry is actually the value and the first entry is the Address of the next element and so long story short this address Points To here and this address points to here and this address points to here And then this address is a null pointer. So it means that you're at the end of your linked list okay, cool last example I wanted to show was the leak checking and so basically this just uses The heap Evaluation function which simply returns the number of chunks and bytes of Allocated space on the heap and So here you can see we were just initializing our heap Allocating some random 24 byte chunks then checking the heap to see the current status And printing the results out. And so if I run this code You can see here. It says, you know, the heap contains three allocated chunks and 80 allocated bytes With zero free space on the heap and so if this if you had a bunch of You know heap management Stuff running in in your assembly code and this was the last thing that happened, you know in your code And you ran this heap evaluation. This would suggest that you had some kind of a memory leak because You're ending your program with some number of allocated chunks on the heap and allocated bytes Where in reality and ideally you would have these values all be zero You would have freed all of your allocated bytes and chunks by the time your program ends Not that you have to but that just if you didn't that would mean that you Had some loose ends that you didn't end up tying up So with that out of the way, that's pretty much the examples I wanted to cover at this point we've gone through pretty much all the basics for heap management Allocations freeing of chunks as well as our very rudimentary You know low IQ implementation of a of a heap with this kind of header and footer Where inside the header in the footer? We're also encoding whether or not the chunks are allocated or not As well as we covered some potential examples as as well as a linked list and how you might use a linked list Indynamic the allocated memory With that out of the way, I'll see you in the next video