feat: add bitmap

This commit is contained in:
Yadunand Prem 2023-11-04 18:42:37 +08:00
parent 4a3e0a2fb7
commit c6633b8f4c
No known key found for this signature in database
10 changed files with 719 additions and 0 deletions

View File

@ -0,0 +1,19 @@
.PHONY: run
bitmap: bitmap.c testmap.c
gcc $^ -o $@
run-bitmap: bitmap
./bitmap
mymalloc: mymalloc.c testmalloc.c bitmap.c
gcc $^ -o $@
run-mymalloc: mymalloc
./mymalloc
harness: harness.c mymalloc.c bitmap.c
gcc $^ -o $@
run-harness: harness
./harness

View File

@ -0,0 +1,115 @@
#include "bitmap.h"
#include <stdio.h>
// IMPLEMENTED FOR YOU
// Utility function to print out an array of char as bits
// Print the entire bitmap. Arguments: The bitmap itself, and the length of the
// bitmap Each bit of the bitmap represents 1 byte of memory. 0 = free, 1 =
// allocated. map: The bitmap itself, an array of unsigned char.
// Each bit of the bitmap represents one byte of memory
// len: The length of the bitmap array in characters
//
// Returns: Nothing
void print_map(unsigned char *map, int len) {
int i, j;
for (i = 0; i < len; i++) {
unsigned char mask = 0b10000000;
for (j = 0; j < 8; j++) {
if (map[i] & mask)
printf("1");
else
printf("0");
mask = mask >> 1;
}
printf(" ");
}
printf("\n");
}
// Search the bitmap for the required number of zeroes (representing
// free bytes of memory). Returns index of first stretch of 0s
// that meet the criteria. You can use this as an index into
// an array of char that represents the process heap
// bitmap = Bitmap declared as an array of unsigned char
// len = Length of bitmap in characters
// num_zeroes = Length of string of 0's required
// Returns: Index to stretch of 0's of required length, -1 if no such stretch
// can be found
long search_map(unsigned char *bitmap, int len, long num_zeroes) {
int count = 0;
for (int i = 0; i < len; i++) {
unsigned char mask = 0b10000000;
for (int j = 0; j < 8; j++) {
if ((bitmap[i] & mask) == 0) {
count++;
if (count == num_zeroes) {
return i * 8 + j - num_zeroes + 1;
}
} else {
count = 0;
}
mask >>= 1;
}
}
return -1;
} // main
// Set map bits to 0 or 1 depending on whether value is non-zero
// map = Bitmap, declared as an array of unsigned char
// start = Starting index to mark as 1 or 0
// length = Number of bits to mark
// value = Value to mark the bits as. value = 0 marks the bits
// as 0, non-zero marks the bits as 1
// Returns: Nothing
// start: 7
// length: 4
// value: 1
void set_map(unsigned char *map, long start, long length, int value) {
// printf("set_map with start=%ld, length=%ld val=%d\n", start, length,
// value);
int starting_index = start / 8;
for (int i = 0; i < length; i++) {
int currIdx = (start + i) / 8;
int currOffset = (start + i) % 8;
int mask = 0b10000000;
if (value == 0) {
map[currIdx] &= ~(mask >> currOffset);
} else {
map[currIdx] |= (mask >> currOffset);
}
}
// the ~(1<< offset) is a nice trick to create, for example
// ~(1 << 2) = 1111 1011
// By anding this, you can set the value to 0 for that bit
// map[char_index + length_index] &= ~(1 << length_offset);
// the (1<< offset) is a nice trick to create, for example
// (1 << 2) = 0000 0100
// likewise, set the given bit to 1
// map[char_index + length_index] |= (1 << length_offset);
}
// IMPLEMENTED FOR YOU
// Marks a stretch of bits as "1", representing allocated memory
// map = Bitmap declared as array of unsigned char
// start = Starting index to mark
// length = Number of bits to mark as "1"
void allocate_map(unsigned char *map, long start, long length) {
set_map(map, start, length, 1);
}
// IMPLEMENTED FOR YOU
// Marks a stretch of bits as "0", representing allocated memory
// map = Bitmap declared as array of unsigned char
// start = Starting index to mark
// length = Number of bits to mark as "0"
void free_map(unsigned char *map, long start, long length) {
set_map(map, start, length, 0);
}

View File

@ -0,0 +1,40 @@
/* Bitmap management
The bitmap should be an array of unsigned char.
*/
// Used by test harness
long get_index(void *ptr);
// Utility function to print out an array of char as bits
// Print the entire bitmap. Arguments: The bitmap itself, and the length of the
// bitmap Each bit of the bitmap represents 1 byte of memory. 0 = free, 1 =
// allocated. map: The bitmap itself, an array of unsigned char.
// Each bit of the bitmap represents one byte of memory
// len: The length of the bitmap array in characters
//
// Returns: Nothing
void print_map(unsigned char *map, int len);
// Search the bitmap for the required number of zeroes (representing
// free bytes of memory). Returns index of first stretch of 0s
// that meet the criteria. You can use this as an index into
// an array of char that represents the process heap
// bitmap = Bitmap declared as an array of unsigned char
// len = Length of bitmap in characters
// num_zeroes = Length of string of 0's required
// Returns: Index to stretch of 0's of required length
long search_map(unsigned char *bitmap, int len, long num_zeroes);
// Marks a stretch of bits as "1", representing allocated memory
// map = Bitmap declared as array of unsigned char
// start = Starting index to mark
// length = Number of bits to mark as "1"
void allocate_map(unsigned char *map, long start, long length);
// Marks a stretch of bits as "0", representing allocated memory
// map = Bitmap declared as array of unsigned char
// start = Starting index to mark
// length = Number of bits to mark as "0"
void free_map(unsigned char *, long, long);

View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <assert.h>
#include "mymalloc.h"
void *runmalloc(size_t len, long expected_ndx) {
long ndx;
void *ptr;
printf("Allocate %ld bytes.\n", len);
printf("Before: ");
print_memlist();
ptr = mymalloc(len);
printf("After: ");
print_memlist();
ndx = get_index((void *) ptr);
printf("\n");
assert(ndx == expected_ndx);
return ptr;
}
void runfree(void *ptr) {
printf("Free\n");
printf("Before: ");
print_memlist();
myfree(ptr);
printf("After: ");
print_memlist();
printf("\n");
}
int main() {
void *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
ptr1 = runmalloc(4, 0);
ptr2 = runmalloc(32, 4);
ptr3 = runmalloc(2, 36);
runfree(ptr2);
ptr2 = runmalloc(12, 4);
ptr4 = runmalloc(24, 38);
ptr5 = runmalloc(18, 16);
runfree(ptr1);
ptr1 = runmalloc(2, 0);
runfree(ptr1);
runfree(ptr2);
runfree(ptr3);
runfree(ptr4);
runfree(ptr5);
}

View File

@ -0,0 +1,239 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "llist.h"
//#define DEBUG // Enable debug printing
// Debug printer
void dbprintf(char *format, ...) {
#ifdef DEBUG
va_list args;
va_start(args, format);
vprintf(format, args);
#endif
}
// Implements a double linked list.
// Create a new node. You need to
// Create your own TData node, populate
// it, then create a new node with a suitable
// key. Insertion into the link list is
// by ascending order of the key. An example key
// might be the starting address of a memory segment.
TNode *make_node(unsigned int key, TData *data) {
TNode *node = malloc(sizeof(TNode));
node->key = key;
node->pdata = data;
node->prev = NULL;
node->next = NULL;
return node;
}
// Inserts a node into the correct point of the
// double linked list. The list is sorted
// in ascending order of the key. Duplicate keys
// are permitted, though not recommended.
// llist = Pointer to link list
// node = Pointer to node created by make_node
// dir = 0: Insert in ascending order
// dir = 1: Insert in descending order
void insert_node(TNode **llist, TNode *node, int dir) {
if(*llist == NULL) {
*llist = node;
(*llist)->trav = *llist;
(*llist)->tail = *llist;
}
else
if(((*llist)->key >= node->key && dir == 0) || (((*llist)->key <= node->key) && dir == 1)) {
node->next = *llist;
(*llist)->prev = node;
*llist = node;
(*llist)->trav = *llist;
}
else
{
TNode *trav = *llist;
if(dir == 0)
while(trav->next != NULL && trav->key < node->key)
trav = trav->next;
else if(dir == 1)
while(trav->next != NULL && trav->key > node->key)
trav = trav->next;
if(trav->next == NULL && ((trav->key < node->key && dir == 0) ||
(trav->key > node->key && dir == 1))) {
trav->next = node;
node->prev = trav;
// Set the tail
(*llist)->tail = node;
} else {
// Insert into the previous space
node->next = trav;
if(trav->prev != NULL) {
trav->prev->next = node;
node->prev = trav->prev;
}
if(trav->next != NULL) {
}
trav->prev = node;
}
}
}
// Remove a given node from the linked list
void delete_node(TNode **llist, TNode *node) {
if(*llist == NULL || node == NULL)
return;
if((*llist)->key == node->key) {
// Node to be deleted is at the front of the list.
*llist = (*llist)->next;
// Ensure that we don't point to it anymore.
if(*llist != NULL)
(*llist)->prev = NULL;
}
else
{
TNode *trav = *llist;
while(trav != NULL && trav->key != node->key)
trav = trav->next;
// We've found the deletion point
if(trav != NULL) {
trav->prev->next = trav->next;
if(trav->next != NULL)
trav->next->prev = trav->prev;
else
(*llist)->tail = trav->prev;
}
}
free(node);
}
// Find a node that has the value of key
// If there are duplicate keys, the first one encountered
// will be returned.
TNode *find_node(TNode *llist, unsigned int key) {
if(llist == NULL)
return NULL;
TNode *trav = llist;
while(trav != NULL && trav->key != key)
trav = trav->next;
return trav;
}
// Merge the node provided with either the node after or the node before.
// You need to manage merging the data in node->pdata yourself. This code just
// deletes the larger of the two nodes.
// dir = 0: Merge with node before
// dir = 1: Merge with node after
void merge_node(TNode *llist, TNode *node, int dir) {
if(dir == 0) {
if(node->prev == NULL)
return;
delete_node(&llist, node);
}
else
if(dir == 1) {
if(node->next == NULL)
return;
delete_node(&llist, node->next);
}
}
// Go over every element of llist, and call func
// func prototype is void func(TNode *);
void process_list(TNode *llist, void (*func)(TNode *)) {
TNode *trav = llist;
while(trav) {
func(trav);
trav = trav->next;
}
}
// Purge the entire list. You must
// free any dynamic data in the TData
// struct yourself.
void purge_list(TNode **llist) {
TNode *trav = *llist, *tmp;
while(trav) {
tmp = trav->next;
free(trav);
trav = tmp;
}
*llist = NULL;
}
// Reset traverser
// where=0 START: Resets traverser to start of list
// where=1 END: Rsets
void reset_traverser(TNode *llist, int where)
{
if(llist == NULL)
return;
if(where == FRONT)
llist->trav = llist;
else
if(where == REAR)
llist->trav = llist->tail;
}
// Get the next node
TNode *succ(TNode *llist)
{
if(llist == NULL)
return NULL;
TNode *ret = llist->trav;
if(llist->trav != NULL)
llist->trav = llist->trav->next;
return ret;
}
// Get the previous node
TNode *pred(TNode *llist)
{
if(llist == NULL)
return NULL;
TNode *ret = llist->trav;
if(llist->trav != NULL)
llist->trav = llist->trav->prev;
return ret;
}

View File

@ -0,0 +1,113 @@
// Uncomment the next line to enable debug printing
#define DEBUG // Enable debug printing
// The debug printer; used like a normal printf, except
// that printing can be turned off by commenting out the
// #define DEBUG above.
void dbprintf(char *format, ...);
// You should modify this structure to hold
// whatever you need to implement your
// memory manager
typedef struct td {
size_t len;
} TData;
/* -----------------------------------------
BASIC ROUTINES
Basic linked list routines
---------------------------------------- */
// Basic double linked list node.
typedef struct tn {
unsigned int key;
TData *pdata; // Pointer to the data you want to store
struct tn *trav; // Only used in the root for traversal
struct tn *tail; // Only used in the root for finding the end of the list
struct tn *prev;
struct tn *next;
} TNode;
// Insert Direction
#define ASCENDING 0
#define DESCENDING 1
// Merge direction
#define PRECEDING 0
#define SUCCEEDING 1
// Traverser position
#define FRONT 0
#define REAR 1
// Create a new node. You need to
// Create your own TData node, populate
// it, then create a new node with a suitable
// key. Insertion into the link list is
// by ascending order of the key. An example key
// might be the starting address of a memory segment.
TNode *make_node(unsigned int key, TData *data);
// Inserts a node into the correct point of the
// double linked list. The list is sorted
// in ascending order of the key. Duplicate keys
// are permitted, though not recommended.
// llist = Pointer to link list
// node = Pointer to node created by make_node
// dir = 0: Insert in ascending order
// dir = 1: Insert in descending order
void insert_node(TNode **llist, TNode *node, int dir);
// Remove a given node from the linked list
void delete_node(TNode **llist, TNode *node);
// Find a node that has the value of key
// If there are duplicate keys, the first one encountered
// will be returned.
TNode *find_node(TNode *llist, unsigned int key);
// Merge the node provided with either the node after or the node before.
// You need to manage merging the data in node->pdata yourself. This code just
// deletes the larger of the two nodes.
// dir = 0: Merge with node before
// dir = 1: Merge with node after
void merge_node(TNode *llist, TNode *node, int dir);
// Purge the entire list. You must
// free any dynamic data in the TData
// struct yourself.
void purge_list(TNode **llist);
/* -----------------------------------------
TRAVERSAL ROUTINES
Lets you traverse the linked list
---------------------------------------- */
// Go over every element of llist, and call func
// func prototype is void func(TNode *);
void process_list(TNode *llist, void (*func)(TNode *));
// Reset traverser
// where=0 START: Resets traverser to start of list
// where=1 END: Rsets
void reset_traverser(TNode *llist, int where);
// Get the next node
TNode *succ(TNode *llist);
// Get the previous node
TNode *pred(TNode *llist);

View File

@ -0,0 +1,40 @@
#include "mymalloc.h"
#include "bitmap.h"
#include <stdio.h>
#include <stdlib.h>
char _heap[MEMSIZE] = {0};
unsigned char _bitmap[BITMAP_SIZE] = {};
char lens[MEMSIZE] = {0};
// Do not change this. Used by the test harness.
// You may however use this function in your code if necessary.
long get_index(void *ptr) {
if (ptr == NULL)
return -1;
else
return (long)((char *)ptr - &_heap[0]);
}
void print_memlist() { print_map(_bitmap, BITMAP_SIZE); }
// Allocates size bytes of memory and returns a pointer
// to the first byte.
void *mymalloc(size_t size) {
long start = search_map(_bitmap, BITMAP_SIZE, size);
if (start == -1)
return NULL;
allocate_map(_bitmap, start, size);
lens[start] = size;
return (void *)(&_heap[start]);
}
// Frees memory pointer to by ptr.
void myfree(void *ptr) {
if (ptr == NULL)
return;
long ptrIdx = get_index(ptr);
long size = lens[ptrIdx];
free_map(_bitmap, ptrIdx, size);
lens[ptrIdx] = 0;
}

View File

@ -0,0 +1,12 @@
#include "stdio.h"
#define MEMSIZE 64 // Size of memory in bytes
#define BITMAP_SIZE 8 // Size of Bitmap
// Used by the test harness
long get_index(void *ptr);
// Debugging routine
void print_memlist();
void *mymalloc(size_t);
void myfree(void *);

View File

@ -0,0 +1,35 @@
#include "mymalloc.h"
#include <stdio.h>
void testalloc(long size, char *ptrname, char **ptr) {
printf("\nAllocating %ld bytes to %s\n", size, ptrname);
*ptr = mymalloc(size);
if (*ptr == NULL)
printf("Allocation failed.\n");
print_memlist();
}
void testfree(char *ptr, char *ptrname) {
printf("\nFreeing %s\n", ptrname);
myfree(ptr);
print_memlist();
}
int main() {
char *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
testalloc(4, "ptr1", &ptr1);
testalloc(32, "ptr2", &ptr2);
testalloc(4, "ptr3", &ptr3);
testfree(ptr2, "ptr2");
testalloc(24, "ptr2", &ptr2);
testalloc(6, "ptr4", &ptr4);
testfree(ptr2, "ptr2");
testalloc(32, "ptr5", &ptr5);
testfree(ptr1, "ptr1");
testfree(ptr2, "ptr2");
testfree(ptr3, "ptr3");
testfree(ptr4, "ptr4");
testfree(ptr5, "ptr5");
}

View File

@ -0,0 +1,54 @@
#include <stdio.h>
#include <assert.h>
#include "bitmap.h"
#define LEN 5
void print_result(unsigned char *map, int len, long length, long expected) {
long ndx = search_map(map, len, length);
printf("Length: %ld, Expected: %ld, Actual: %ld\n", length, expected, ndx);
// This will cause a crash if the index returned does not match what is expected
assert(ndx == expected);
}
int main() {
unsigned char map[LEN] = {0b11001000, 0b00011111, 0b00001100, 0b11000000, 0b00000001};
int ndx;
printf("\n");
print_result(map, LEN, 2, 2);
print_result(map, LEN, 4, 5);
print_result(map, LEN, 6, 5);
print_result(map, LEN, 12, 26);
print_result(map, LEN, 128, -1);
// Allocate map
printf("\nAllocating 2 bytes\n");
printf("BEFORE: ");
print_map(map, LEN);
ndx = search_map(map, LEN, 2);
allocate_map(map, ndx, 2);
printf("AFTER: ");
print_map(map, LEN);
printf("\n");
printf("Allocating 12 bytes\n");
printf("BEFORE: ");
print_map(map, LEN);
ndx = search_map(map, LEN, 12);
allocate_map(map, ndx, 12);
printf("AFTER: ");
print_map(map, LEN);
printf("\n");
print_result(map, LEN, 12, -1);
printf("Freeing 12 bytes\n");
printf("BEFORE: ");
print_map(map, LEN);
free_map(map, ndx, 12);
printf("AFTER: ");
print_map(map, LEN);
printf("\n");
print_result(map, LEN, 12, 26);
}