1 //////////////////////////////////////////////////////////////////////////////
4 // ADLib, Prop and their related set of tools and documentation are in the
5 // public domain. The author(s) of this software reserve no copyrights on
6 // the source code and any code generated using the tools. You are encouraged
7 // to use ADLib and Prop to develop software, in both academic and commercial
8 // settings, and are free to incorporate any part of ADLib and Prop into
11 // Although you are under no obligation to do so, we strongly recommend that
12 // you give away all software developed using our tools.
14 // We also ask that credit be given to us when ADLib and/or Prop are used in
15 // your programs, and that this notice be preserved intact in all the source
18 // This software is still under development and we welcome any suggestions
19 // and help from the users.
23 //////////////////////////////////////////////////////////////////////////////
27 #include <stddef.h> // for offsetof
28 #include <AD/memory/boundtag.h> // Boundary tag memory manager
30 //////////////////////////////////////////////////////////////////////////////
31 // Constructor: initialize the memory manager. The free list initially
32 // contains one ``stub'' element.
33 //////////////////////////////////////////////////////////////////////////////
34 BoundaryTag::BoundaryTag(long size
) : Mem("BoundaryTag"), pages(0)
35 { pageSize
= size
> 256 ? size
: 256; // The default page size
39 BoundaryTag::BoundaryTag(Mem
& m
, long size
) : Mem(m
, "BoundaryTag"), pages(0)
40 { pageSize
= size
> 256 ? size
: 256; // The default page size
43 //////////////////////////////////////////////////////////////////////////////
44 // Destructor: frees all the pages allocated
45 //////////////////////////////////////////////////////////////////////////////
46 BoundaryTag::~BoundaryTag() { clear(); }
48 //////////////////////////////////////////////////////////////////////////////
49 // Release all memory from the manager
50 //////////////////////////////////////////////////////////////////////////////
51 void BoundaryTag::clear()
52 { Page
* current
, * next
;
53 for (current
= pages
; current
; current
= next
) {
55 manager_mem
->free(current
);
57 pages
= NULL
; // Linked list of pages
58 freeList
= stub
.next
= stub
.last
= &stub
; // Dummy element
59 stub
.size
= stub
.last_size
= 0;
63 //////////////////////////////////////////////////////////////////////////////
64 // Allocate a new string from the manager.
65 //////////////////////////////////////////////////////////////////////////////
66 char * BoundaryTag::operator [] (const char * string
)
67 { char * newString
= (char *) (*this)[strlen(string
) + 1];
68 strcpy(newString
,string
);
72 //////////////////////////////////////////////////////////////////////////////
73 // Allocate a chunk of storage from the memory manager
74 //////////////////////////////////////////////////////////////////////////////
75 void * BoundaryTag::m_alloc(size_t size
)
76 { register unsigned long elements
=
77 (size
+ 2*sizeof(Block
) - offsetof(Block
,next
)-1) / sizeof(Block
);
78 register Block
* B
, * start
;
81 // Use first fit strategy starting from the free list
84 for (B
= start
= freeList
; ;B
= B
->next
) {
85 if (B
->size
>= elements
) { // Size fitting??
86 if (B
->size
- elements
< 2) { // Split block or not??
88 // The block should not be split. Mark the block as allocated,
89 // unlink it from the free list and returns the free block
90 // to the client. We'll also advance the free list to the
91 // next block after the current block so that we will not
92 // stuck at one end of the free list.
94 B
[B
->size
].last_size
|= Block::USED_BIT
;
95 B
->size
|= Block::USED_BIT
;
96 B
->next
->last
= B
->last
;
97 B
->last
->next
= B
->next
;
102 // The block will be split. We'll keep the top chunk
103 // and return the bottom chunk to the client. Nothing
104 // will have to be deleted from the free list.
106 register long leftOver
= B
->size
- elements
;
107 register Block
* newBlock
= B
+ leftOver
;
108 newBlock
[elements
].last_size
=
109 newBlock
->size
= elements
| Block::USED_BIT
;
110 newBlock
->last_size
= B
->size
= leftOver
;
112 return &newBlock
->next
;
115 if (B
->next
== start
) break; // back to the start??
117 this->grow(size
); // Allocate a new page
121 //////////////////////////////////////////////////////////////////////////////
122 // This auxiliary function allocate a new page and puts it onto the
124 //////////////////////////////////////////////////////////////////////////////
125 void BoundaryTag::grow(long bytes
)
127 if (bytes
< pageSize
) bytes
= pageSize
;
129 (bytes
+ 2*sizeof(Block
) - offsetof(Block
,next
)-1) / sizeof(Block
);
131 Page
* newPage
= (Page
*)
132 manager_mem
->m_alloc(sizeof(Page
) + elements
* sizeof(Block
));
133 Block
* B
= newPage
->blocks
;
135 newPage
->next
= pages
; // Chain the pages together
138 allocated
+= elements
* sizeof(Block
);
141 // The new page is split into two blocks. One is of size |elements|
142 // and the other is of size 1 and is located just after the first
143 // block. The latter block is a stub and is marked with the used bit
144 // so that it will not be accidently merged with the first.
147 B
[elements
].last_size
= B
->size
= elements
;
148 B
[elements
].size
= B
->last_size
= (LongWord
)Block::USED_BIT
| 1;
151 // Link the new block |B| so that it is located after the
152 // current starting point of the free list
155 B
->next
= freeList
->next
;
159 freeList
= B
; // Point the free list directly to the new block
160 // so that we'll won't have to search for it
164 //////////////////////////////////////////////////////////////////////////////
165 // Free block, combines it with adjacent blocks if that is possible.
166 //////////////////////////////////////////////////////////////////////////////
167 void BoundaryTag::free(void * core
)
169 register Block
* B
= (Block
*) (((char *)core
) - offsetof(Block
,next
));
170 register Block
* aBlock
;
172 assert( (B
->size
& Block::USED_BIT
) == Block::USED_BIT
);
174 B
->size
&= ~Block::USED_BIT
; // Unmark the used bit
175 aBlock
= B
+ B
->size
; // This is the next block
176 if ( (aBlock
->size
& Block::USED_BIT
) == 0) { // Merge with next block??
177 aBlock
->next
->last
= aBlock
->last
; // Unlink the next block
178 aBlock
->last
->next
= aBlock
->next
;
179 if (aBlock
== freeList
) freeList
= aBlock
->next
;
180 B
->size
+= aBlock
->size
; // and merge
181 B
[B
->size
].last_size
= B
->size
;
183 aBlock
->last_size
&= ~Block::USED_BIT
;
186 if ( (B
->last_size
& Block::USED_BIT
) == 0) { // Merge with last block??
187 aBlock
= B
- B
->last_size
; // This is the last block
188 aBlock
->size
+= B
->size
; // and merge
189 aBlock
[aBlock
->size
].last_size
= aBlock
->size
;
193 // Now, merges |B|, the (possibly coalesed) free block,
194 // to the position {\em before} the current starting point
195 // of the free list. This way there is time before this same
196 // block is encountered again. This gives this block more opportunity
197 // to be merged with some other adjacent blocks, which might be
198 // freed during this time.
201 B
->last
= freeList
->last
;
207 //////////////////////////////////////////////////////////////////////////////
208 // Compute allocation statistics of the memory manager.
209 //////////////////////////////////////////////////////////////////////////////
210 BoundaryTag::Statistics
BoundaryTag::statistics() const
212 register Page
* page
;
215 S
.bytes_allocated
= allocated
;
217 // Count the number of pages allocated
219 for (S
.page_count
= 0, page
= pages
; page
; page
= page
->next
)
223 // Count the number of free blocks within freelist and compute
224 // the amount of bytes free
226 for (S
.free_block_count
= 0, S
.bytes_available
= 0,
227 B
= freeList
; ; B
= B
->next
) {
228 S
.bytes_available
+= B
->size
;
229 S
.free_block_count
++;
230 if (B
->next
== freeList
) break;
233 S
.bytes_available
*= sizeof(Block
);
238 //////////////////////////////////////////////////////////////////////////////
239 // Returns the size of an allocated block
240 //////////////////////////////////////////////////////////////////////////////
241 size_t BoundaryTag::size(const void * core
) const
242 { register Block
* B
= (Block
*) (((char *)core
) - offsetof(Block
,next
));
243 assert( (B
->size
& Block::USED_BIT
) == Block::USED_BIT
);
244 return B
->size
& ~Block::USED_BIT
;