|
|
1.1 root 1: \ tag: forth memory allocation
2: \
3: \ Copyright (C) 2002-2003 Stefan Reinauer
4: \
5: \ See the file "COPYING" for further information about
6: \ the copyright and warranty status of this work.
7: \
8:
9: \ 7.3.3.2 memory allocation
10:
11: \ these need to be initialized by the forth kernel by now.
12: variable start-mem 0 start-mem ! \ start of memory
13: variable end-mem 0 end-mem ! \ end of memory
14: variable free-list 0 free-list ! \ free list head
15:
16: \ initialize necessary variables and write a valid
17: \ free-list entry containing all of the memory.
18: \ start-mem: pointer to start of memory.
19: \ end-mem: pointer to end of memory.
20: \ free-list: head of linked free list
21:
22: : init-mem ( start-addr size )
23: over dup
24: start-mem ! \ write start-mem
25: free-list ! \ write first freelist entry
26: 2dup /n - swap ! \ write 'len' entry
27: over cell+ 0 swap ! \ write 'next' entry
28: + end-mem ! \ write end-mem
29: ;
30:
31: \ --------------------------------------------------------------------
32:
33: \ return pointer to smallest free block that contains
34: \ at least nb bytes and the block previous the the
35: \ actual block. On failure the pointer to the smallest
36: \ free block is 0.
37:
38: : smallest-free-block ( nb -- prev ptr | 0 0 )
39: 0 free-list @
40: fffffff 0 0 >r >r >r
41: begin
42: dup
43: while
44: ( nb prev pp R: best_nb best_pp )
45: dup @ 3 pick r@ within if
46: ( nb prev pp )
47: r> r> r> 3drop \ drop old smallest
48: 2dup >r >r dup @ >r \ new smallest
49: then
50: nip dup \ prev = pp
51: cell + @ \ pp = pp->next
52: repeat
53: 3drop r> drop r> r>
54: ;
55:
56:
57: \ --------------------------------------------------------------------
58:
59: \ allocate size bytes of memory
60: \ return pointer to memory (or throws an exception on failure).
61:
62: : alloc-mem ( size -- addr )
63:
64: \ make it legal (and fast) to allocate 0 bytes
65: dup 0= if exit then
66:
67: aligned \ keep memory aligned.
68: dup smallest-free-block \ look up smallest free block.
69:
70: dup 0= if
71: \ 2drop
72: -15 throw \ out of memory
73: then
74:
75: ( al-size prev addr )
76:
77: \ If the smallest fitting block found is bigger than
78: \ the size of the requested block plus 2*cellsize we
79: \ can split the block in 2 parts. otherwise return a
80: \ slightly bigger block than requested.
81:
82: dup @ ( d->len ) 3 pick cell+ cell+ > if
83:
84: \ splitting the block in 2 pieces.
85: \ new block = old block + len field + size of requested mem
86: dup 3 pick cell+ + ( al-size prev addr nd )
87:
88: \ new block len = old block len - req. mem size - 1 cell
89: over @ ( al-size prev addr nd addr->len )
90: 4 pick ( ... al-size )
91: cell+ - ( al-size prev addr nd nd nd->len )
92: over ! ( al-size prev addr nd )
93:
94: over cell+ @ ( al-size prev addr nd addr->next )
95: \ write addr->next to nd->next
96: over cell+ ! ( al-size prev addr nd )
97: over 4 pick swap !
98: else
99: \ don't split the block, it's too small.
100: dup cell+ @
101: then
102:
103: ( al-size prev addr nd )
104:
105: \ If the free block we got is the first one rewrite free-list
106: \ pointer instead of the previous entry's next field.
107: rot dup 0= if drop free-list else cell+ then
108: ( al-size addr nd prev->next|fl )
109: !
110: nip cell+ \ remove al-size and skip len field of returned pointer
111:
112: ;
113:
114:
115: \ --------------------------------------------------------------------
116:
117: \ free block given by addr. The length of the
118: \ given block is stored at addr - cellsize.
119: \
120: \ merge with blocks to the left and right
121: \ immediately, if they are free.
122:
123: : free-mem ( addr len -- )
124:
125: \ we define that it is legal to free 0-byte areas
126: 0= if drop exit then
127: ( addr )
128:
129: \ check if the address to free is somewhere within
130: \ our available memory. This fails badly on discontigmem
131: \ architectures. If we need more RAM than fits on one
132: \ contiguous memory area we are too bloated anyways. ;)
133:
134: dup start-mem @ end-mem @ within 0= if
135: \ ." free-mem: no such memory: 0x" u. cr
136: exit
137: then
138:
139: /n - \ get real block address
140: 0 free-list @ ( addr prev l )
141:
142: begin \ now scan the free list
143: dup 0<> if \ only check len, if block ptr != 0
144: dup dup @ cell+ + 3 pick <
145: else
146: false
147: then
148: while
149: nip dup \ prev=l
150: cell+ @ \ l=l->next
151: repeat
152:
153: ( addr prev l )
154:
155: dup 0<> if \ do we have free memory to merge with?
156:
157: dup dup @ cell+ + 3 pick = if \ hole hit. adding bytes.
158: \ freeaddr = end of current block -> merge
159: ( addr prev l )
160: rot @ cell+ ( prev l f->len+cellsize )
161: over @ + \ add l->len
162: over ! ( prev l )
163: swap over cell+ @ \ f = l; l = l->next;
164:
165: \ The free list is sorted by addresses. When merging at the
166: \ start of our block we might also want to merge at the end
167: \ of it. Therefore we fall through to the next border check
168: \ instead of returning.
169: true \ fallthrough value
170: else
171: false \ no fallthrough
172: then
173: >r \ store fallthrough on ret stack
174:
175: ( addr prev l )
176:
177: dup 3 pick dup @ cell+ + = if \ hole hit. real merging.
178: \ current block starts where block to free ends.
179: \ end of free block addr = current block -> merge and exit
180: ( addr prev l )
181: 2 pick dup @ ( f f->len )
182: 2 pick @ cell+ + ( f newlen )
183: swap ! ( addr prev l )
184: 3dup drop
185: 0= if
186: free-list
187: else
188: 2 pick cell+
189: then ( value prev->next|free-list )
190: ! ( addr prev l )
191: cell+ @ rot ( prev l->next addr )
192: cell+ ! drop
193: r> drop exit \ clean up return stack
194: then
195:
196: r> if 3drop exit then \ fallthrough? -> exit
197: then
198:
199: \ loose block - hang it before current.
200:
201: ( addr prev l )
202:
203: \ hang block to free in front of the current entry.
204: dup 3 pick cell+ ! \ f->next = l;
205: free-list @ = if \ is block to free new list head?
206: over free-list !
207: then
208:
209: ( addr prev )
210: dup 0<> if \ if (prev) prev->next=f
211: cell+ !
212: else
213: 2drop \ no fixup needed. clean up.
214: then
215:
216: ;
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.