alloc.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #include <unistd.h>
  2. #include <stdint.h>
  3. #include <string.h>
  4. #include <limits.h>
  5. #include <assert.h>
  6. #include "alloc.h"
  7. /* FIXME: Doesn't handle non-page-aligned poolsize. */
  8. /* FIXME: Doesn't handle sub-page allocations. */
  9. #define MIN_SIZE (getpagesize() * 2)
  10. enum page_state
  11. {
  12. FREE,
  13. TAKEN,
  14. TAKEN_START,
  15. };
  16. void alloc_init(void *pool, unsigned long poolsize)
  17. {
  18. uint8_t *bits = pool;
  19. unsigned int pages = poolsize / getpagesize();
  20. if (poolsize < MIN_SIZE)
  21. return;
  22. memset(bits, 0, (pages * 2 + CHAR_BIT - 1)/ CHAR_BIT);
  23. }
  24. static enum page_state get_page_state(const uint8_t *bits, unsigned long page)
  25. {
  26. return bits[page * 2 / CHAR_BIT] >> (page * 2 % CHAR_BIT) & 3;
  27. }
  28. static void set_page_state(uint8_t *bits, unsigned long page, enum page_state s)
  29. {
  30. bits[page * 2 / CHAR_BIT] &= ~(3 << (page * 2 % CHAR_BIT));
  31. bits[page * 2 / CHAR_BIT] |= ((uint8_t)s << (page * 2 % CHAR_BIT));
  32. }
  33. static unsigned long metadata_length(void *pool, unsigned long poolsize)
  34. {
  35. return ((poolsize / getpagesize() * 2 / CHAR_BIT) + getpagesize() - 1)
  36. & ~(getpagesize() - 1);
  37. }
  38. void *alloc_get(void *pool, unsigned long poolsize,
  39. unsigned long size, unsigned long align)
  40. {
  41. long i;
  42. unsigned long free, want, metalen;
  43. if (poolsize < MIN_SIZE)
  44. return NULL;
  45. /* FIXME: Necessary for this. */
  46. if (size == 0)
  47. size = 1;
  48. want = (size + getpagesize() - 1) / getpagesize();
  49. metalen = metadata_length(pool, poolsize);
  50. free = 0;
  51. /* We allocate from far end, to increase ability to expand metadata. */
  52. for (i = (poolsize - metalen) / getpagesize() - 1; i >= 0; i--) {
  53. switch (get_page_state(pool, i)) {
  54. case FREE:
  55. if (++free >= want) {
  56. unsigned long j;
  57. char *ret = (char *)pool + metalen
  58. + i * getpagesize();
  59. /* They might ask for multi-page alignment. */
  60. if ((unsigned long)ret % align)
  61. continue;
  62. for (j = i+1; j < i + want; j++)
  63. set_page_state(pool, j, TAKEN);
  64. set_page_state(pool, i, TAKEN_START);
  65. return ret;
  66. }
  67. break;
  68. case TAKEN_START:
  69. case TAKEN:
  70. free = 0;
  71. break;
  72. }
  73. }
  74. return NULL;
  75. }
  76. void alloc_free(void *pool, unsigned long poolsize, void *free)
  77. {
  78. unsigned long pagenum, metalen;
  79. if (poolsize < MIN_SIZE)
  80. return;
  81. if (!free)
  82. return;
  83. metalen = metadata_length(pool, poolsize);
  84. assert(free > pool && (char *)pool + poolsize > (char *)free);
  85. assert((unsigned long)free % getpagesize() == 0);
  86. pagenum = ((char *)free - (char *)pool - metalen) / getpagesize();
  87. assert(get_page_state(pool, pagenum) == TAKEN_START);
  88. set_page_state(pool, pagenum, FREE);
  89. while (get_page_state(pool, ++pagenum) == TAKEN)
  90. set_page_state(pool, pagenum, FREE);
  91. }
  92. bool alloc_check(void *pool, unsigned long poolsize)
  93. {
  94. unsigned int i, metalen;
  95. enum page_state last_state = FREE;
  96. if (poolsize < MIN_SIZE)
  97. return true;
  98. metalen = metadata_length(pool, poolsize);
  99. for (i = 0; i < (poolsize - metalen) / getpagesize(); i++) {
  100. enum page_state state = get_page_state(pool, i);
  101. switch (state) {
  102. case FREE:
  103. case TAKEN_START:
  104. break;
  105. case TAKEN:
  106. if (last_state == FREE)
  107. return false;
  108. break;
  109. default:
  110. return false;
  111. }
  112. last_state = state;
  113. }
  114. return true;
  115. }