tiny.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #include "tiny.h"
  2. #include "bitops.h"
  3. #include <assert.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. /* One byte header, one byte data. */
  7. #define MIN_BLOCK_SIZE 2
  8. /* Bit 7 (in any byte) == start or end. */
  9. #define TERM_BIT 0x80
  10. /* Bit 6 (first and last byte) == one byte long. */
  11. #define SINGLE_BYTE 0x40
  12. /* Bit 5 (of first byte) == "is this block free?" */
  13. #define FREE_BIT 0x20
  14. /* Val is usually offset by MIN_BLOCK_SIZE here. */
  15. static unsigned encode_length(unsigned long val)
  16. {
  17. unsigned int bits = fls(val);
  18. /* 5 bits in first byte. */
  19. if (bits <= 5)
  20. return 1;
  21. /* 6 bits in last byte, 7 bits in middle ones. */
  22. return 2 + (bits - 5) / 7;
  23. }
  24. /* Header is included in length, so we might need an extra byte. */
  25. static unsigned encode_len_with_header(unsigned long len)
  26. {
  27. unsigned int hdrlen = 1;
  28. assert(len);
  29. while (encode_length(len + hdrlen - MIN_BLOCK_SIZE) != hdrlen)
  30. hdrlen = encode_length(len + hdrlen - MIN_BLOCK_SIZE);
  31. return hdrlen;
  32. }
  33. /* Encoding can be read from front or back; 0 is invalid at either
  34. * start or end. Returns bytes used for header, or 0 if won't fit. */
  35. static unsigned encode(unsigned long len, bool free, unsigned char arr[],
  36. size_t limit)
  37. {
  38. unsigned int hdrlen = 1;
  39. /* We can never have a length < MIN_BLOCK_SIZE. */
  40. assert(len >= MIN_BLOCK_SIZE);
  41. len -= MIN_BLOCK_SIZE;
  42. if (encode_length(len) > limit)
  43. return 0;
  44. /* First byte always contains free bit. */
  45. arr[0] = TERM_BIT | (free ? FREE_BIT : 0);
  46. /* Also holds 5 bits of data (0 - 31) */
  47. arr[0] |= (len & 0x1F);
  48. len >>= 5;
  49. /* One byte only? */
  50. if (!len) {
  51. arr[0] |= SINGLE_BYTE;
  52. return hdrlen;
  53. }
  54. /* Middle bytes. */
  55. while (len >= (1 << 6)) {
  56. /* Next 7 data bits */
  57. arr[hdrlen++] = (len & 0x7F);
  58. len >>= 7;
  59. }
  60. arr[hdrlen++] = (len | TERM_BIT);
  61. return hdrlen;
  62. }
  63. /* Returns bytes used for header. */
  64. static unsigned decode(unsigned long *len, bool *free, const unsigned char *arr)
  65. {
  66. unsigned int hdrlen = 0, bits = 5;
  67. /* Free flag is in bit 5 */
  68. *free = (arr[hdrlen] & FREE_BIT);
  69. /* Bottom five bits are data. */
  70. *len = (arr[hdrlen] & 0x1f);
  71. if (!(arr[hdrlen++] & SINGLE_BYTE)) {
  72. /* Multi-byte encoding? */
  73. while (!(arr[hdrlen] & TERM_BIT)) {
  74. /* 7 more data bits. */
  75. *len |= (arr[hdrlen] & 0x7fUL) << bits;
  76. hdrlen++;
  77. bits += 7;
  78. }
  79. /* Final byte has 6 bits. */
  80. *len |= (arr[hdrlen] & 0x3fUL) << bits;
  81. hdrlen++;
  82. }
  83. *len += MIN_BLOCK_SIZE;
  84. return hdrlen;
  85. }
  86. /* We keep a recently-freed array, one byte per k. */
  87. static unsigned long free_array_size(unsigned long poolsize)
  88. {
  89. return poolsize / 1024;
  90. }
  91. void tiny_alloc_init(void *pool, unsigned long poolsize)
  92. {
  93. /* We start with free array, and then the rest is free. */
  94. unsigned long arrsize = free_array_size(poolsize);
  95. /* Do nothing with 1 byte or less! */
  96. if (poolsize < MIN_BLOCK_SIZE)
  97. return;
  98. memset(pool, 0, arrsize);
  99. encode(poolsize - arrsize, true,
  100. (unsigned char *)pool + arrsize, poolsize - arrsize);
  101. }
  102. /* Walk through and try to coalesce */
  103. static bool try_coalesce(unsigned char *pool, unsigned long poolsize)
  104. {
  105. unsigned long len, hdrlen, prev_off = 0, prev_len = 0, off;
  106. bool free, prev_free = false, coalesced = false;
  107. off = free_array_size(poolsize);
  108. do {
  109. hdrlen = decode(&len, &free, pool + off);
  110. if (free && prev_free) {
  111. encode(prev_len + len, true, pool + prev_off,
  112. poolsize - prev_off);
  113. coalesced = true;
  114. }
  115. prev_free = free;
  116. prev_off = off;
  117. prev_len = len;
  118. off += len;
  119. } while (off < poolsize);
  120. return coalesced;
  121. }
  122. static bool long_enough(unsigned long offset, unsigned long len,
  123. unsigned long size, unsigned long align)
  124. {
  125. unsigned long end = offset + len;
  126. offset += encode_len_with_header(len);
  127. offset = align_up(offset, align);
  128. return offset + size <= end;
  129. }
  130. void *tiny_alloc_get(void *pool, unsigned long poolsize,
  131. unsigned long size, unsigned long align)
  132. {
  133. unsigned long arrsize = free_array_size(poolsize);
  134. unsigned long len, off, actual, hdr, hdrlen;
  135. bool free;
  136. /* We can't do anything with tiny pools. */
  137. if (poolsize < MIN_BLOCK_SIZE)
  138. return NULL;
  139. /* We don't do zero-allocs; allows 1 more offset in encoding. */
  140. if (!size)
  141. size = 1;
  142. /* FIXME: Look through free array. */
  143. again:
  144. off = arrsize;
  145. hdrlen = decode(&len, &free, (unsigned char *)pool + off);
  146. while (!free || !long_enough(off, len, size, align)) {
  147. /* FIXME: Refill free array if this block is free. */
  148. /* Hit end? */
  149. off += len;
  150. if (off == poolsize) {
  151. if (try_coalesce(pool, poolsize))
  152. goto again;
  153. return NULL;
  154. }
  155. hdrlen = decode(&len, &free, (unsigned char *)pool + off);
  156. }
  157. /* We have a free block. Since we walk from front, take far end. */
  158. actual = ((off + len - size) & ~(align - 1));
  159. hdr = actual - encode_len_with_header(off + len - actual);
  160. /* Do we have enough room to split? */
  161. if (hdr - off >= MIN_BLOCK_SIZE) {
  162. encode(hdr - off, true, (unsigned char *)pool + off, poolsize);
  163. } else {
  164. hdr = off;
  165. }
  166. /* Make sure that we are all-zero up to actual, so we can walk back
  167. * and find header. */
  168. memset((unsigned char *)pool + hdr, 0, actual - hdr);
  169. /* Create header for allocated block. */
  170. encode(off + len - hdr, false, (unsigned char *)pool + hdr, poolsize);
  171. return (unsigned char *)pool + actual;
  172. }
  173. static unsigned char *to_hdr(void *p)
  174. {
  175. unsigned char *hdr = p;
  176. /* Walk back to find end of header. */
  177. while (!*(--hdr));
  178. assert(*hdr & TERM_BIT);
  179. /* Now walk back to find start of header. */
  180. if (!(*hdr & SINGLE_BYTE)) {
  181. while (!(*(--hdr) & TERM_BIT));
  182. }
  183. return hdr;
  184. }
  185. void tiny_alloc_free(void *pool, unsigned long poolsize, void *freep)
  186. {
  187. unsigned char *hdr;
  188. /* Too small to do anything. */
  189. if (poolsize < MIN_BLOCK_SIZE)
  190. return;
  191. hdr = to_hdr(freep);
  192. /* FIXME: Put in free array. */
  193. hdr[0] |= FREE_BIT;
  194. }
  195. unsigned long tiny_alloc_size(void *pool, unsigned long poolsize, void *p)
  196. {
  197. unsigned char *hdr = to_hdr(p);
  198. unsigned long len, hdrlen;
  199. bool free;
  200. hdrlen = decode(&len, &free, hdr);
  201. return len - hdrlen;
  202. }
  203. /* Useful for gdb breakpoints. */
  204. static bool tiny_check_fail(void)
  205. {
  206. return false;
  207. }
  208. bool tiny_alloc_check(void *pool, unsigned long poolsize)
  209. {
  210. unsigned long arrsize = free_array_size(poolsize);
  211. unsigned char *arr = pool;
  212. unsigned long len, off, hdrlen;
  213. bool free;
  214. if (poolsize < MIN_BLOCK_SIZE)
  215. return true;
  216. /* For the moment, free array is all zeroes. */
  217. for (off = 0; off < arrsize; off++) {
  218. if (arr[off] != 0)
  219. return tiny_check_fail();
  220. }
  221. for (off = arrsize; off < poolsize; off += len) {
  222. /* We should have a valid header. */
  223. if (!(arr[off] & TERM_BIT))
  224. return tiny_check_fail();
  225. if (!(arr[off] & SINGLE_BYTE)) {
  226. unsigned long off2;
  227. for (off2 = off+1; off2 < poolsize; off2++) {
  228. if (arr[off2] & TERM_BIT)
  229. break;
  230. }
  231. if (off2 == poolsize)
  232. return tiny_check_fail();
  233. }
  234. hdrlen = decode(&len, &free, arr + off);
  235. if (off + len > poolsize)
  236. return tiny_check_fail();
  237. if (hdrlen != encode_length(len - MIN_BLOCK_SIZE))
  238. return tiny_check_fail();
  239. }
  240. return true;
  241. }
  242. /* FIXME: Implement. */
  243. void tiny_alloc_visualize(FILE *out, void *pool, unsigned long poolsize)
  244. {
  245. }