v / vlib / dlmalloc / dlmalloc.v
1633 lines · 1440 sloc · 35.65 KB · 0832a68bd714695d292aef2ca9b08d16b9a86516
Raw
1// This is a version of dlmalloc.c ported to V. You can find the original
2// source at ftp://g.oswego.edu/pub/misc/malloc.c
3//
4// The original source was written by Doug Lea and released to the public domain
5//
6//
7// # Why dlmalloc?
8//
9// This library does not rely on C code. The primary purpose is for use in freestanding
10// build mode and for WASM target.
11//
12// dlmalloc is not the most performant allocator. It's main purpose is to be
13// easily portable and easy to learn. Here we have straight port of C and dlmalloc-rs
14// versions of dlmalloc.
15module dlmalloc
16
17import math.bits
18
19/*
20$if debug ? {
21 #include "valgrind.h"
22}
23
24fn //C.VALGRIND_MALLOCLIKE_BLOCK(addr voidptr, size usize, rzb usize,is_zeroed bool)
25fn //C.VALGRIND_FREELIKE_BLOCK(addr voidptr, rzB usize)
26fn //C.VALGRIND_MAKE_MEM_UNDEFINED(addr voidptr, size usize)
27*/
28
29pub const n_small_bins = 32
30pub const n_tree_bins = 32
31pub const small_bin_shift = 3
32pub const tree_bin_shift = 8
33
34pub const max_release_check_rate = 4095
35
36fn usize_leading_zeros(x usize) usize {
37 if sizeof(usize) == 8 {
38 return usize(bits.leading_zeros_64(u64(x)))
39 } else {
40 return usize(bits.leading_zeros_32(u32(x)))
41 }
42}
43
44@[inline]
45fn default_granularity() usize {
46 return 64 * 1024
47}
48
49@[inline]
50fn default_trim_threshold() usize {
51 return 2 * 1024 * 1024
52}
53
54@[inline]
55fn malloc_alignment() usize {
56 return sizeof(usize) * 2
57}
58
59@[inline]
60fn chunk_overhead() usize {
61 return sizeof(usize)
62}
63
64@[inline]
65fn min_large_size() usize {
66 return 1 << tree_bin_shift
67}
68
69@[inline]
70fn mmap_chunk_overhead() usize {
71 return 2 * sizeof(usize)
72}
73
74@[inline]
75fn max_small_size() usize {
76 return min_large_size() - 1
77}
78
79@[inline]
80fn max_small_request() usize {
81 return max_small_size() - (malloc_alignment() - 1) - chunk_overhead()
82}
83
84@[inline]
85fn min_chunk_size() usize {
86 return align_up(sizeof(Chunk), malloc_alignment())
87}
88
89@[inline]
90fn chunk_mem_offset() usize {
91 return 2 * sizeof(usize)
92}
93
94@[inline]
95fn min_request() usize {
96 return min_chunk_size() - chunk_overhead() - 1
97}
98
99@[inline]
100fn top_foot_size() usize {
101 return align_offset_usize(chunk_mem_offset()) + pad_request(sizeof(Segment)) + min_chunk_size()
102}
103
104@[inline]
105fn max_request() usize {
106 return calc_max_request()
107}
108
109@[inline]
110fn mmap_foot_pad() usize {
111 return 4 * sizeof(usize)
112}
113
114fn min_sys_alloc_space() usize {
115 return ((~0 - (default_granularity() + top_foot_size() + malloc_alignment()) +
116 1) & ~malloc_alignment()) - chunk_overhead() + 1
117}
118
119fn calc_max_request() usize {
120 x := min_sys_alloc_space()
121 y := (~min_chunk_size() + 1) << 2
122
123 if x < y {
124 return x
125 } else {
126 return y
127 }
128}
129
130fn pad_request(amt usize) usize {
131 return align_up(amt + chunk_overhead(), malloc_alignment())
132}
133
134fn align_offset_usize(addr usize) usize {
135 return align_up(addr, malloc_alignment()) - addr
136}
137
138fn is_aligned(a usize) bool {
139 return a & (malloc_alignment() - 1) == 0
140}
141
142fn is_small(s usize) bool {
143 return s >> small_bin_shift < n_small_bins
144}
145
146fn small_index2size(idx u32) usize {
147 return usize(idx) << small_bin_shift
148}
149
150fn small_index(size usize) u32 {
151 return u32(size >> small_bin_shift)
152}
153
154fn align_up(a usize, alignment usize) usize {
155 if a % alignment == 0 {
156 return a
157 } else {
158 return a - (a % alignment) + alignment
159 }
160}
161
162fn left_bits(x u32) u32 {
163 return (x << 1) | (~(x << 1)) + 1
164}
165
166fn least_bit(x u32) u32 {
167 return x & (~x + 1)
168}
169
170fn leftshift_for_tree_index(x u32) u32 {
171 y := usize(x)
172 if y == n_tree_bins - 1 {
173 return 0
174 } else {
175 return u32(sizeof(usize) * 8 - 1 - ((y >> 1) + tree_bin_shift - 2))
176 }
177}
178
179@[unsafe]
180fn align_as_chunk(ptr_ voidptr) &Chunk {
181 ptr := usize(ptr_)
182 chunk := ptr + chunk_mem_offset()
183 return &Chunk(ptr + align_offset_usize(chunk))
184}
185
186fn request_2_size(req usize) usize {
187 if req < min_request() {
188 return min_chunk_size()
189 } else {
190 return pad_request(req)
191 }
192}
193
194fn overhead_for(c &Chunk) usize {
195 if c.mmapped() {
196 return mmap_chunk_overhead()
197 } else {
198 return chunk_overhead()
199 }
200}
201
202// In order for dlmalloc to efficiently manage memory, it needs a way to communicate with the underlying platform.
203// This `Allocator` type provides an interface for this communication.
204//
205//
206// Why not `interface?` Interfaces require memory allocation so it is simpler to pass a struct.
207pub struct Allocator {
208pub:
209 alloc fn (voidptr, usize) (voidptr, usize, u32) = unsafe { nil }
210 remap fn (voidptr, voidptr, usize, usize, bool) voidptr = unsafe { nil }
211 free_part fn (voidptr, voidptr, usize, usize) bool = unsafe { nil }
212 free_ fn (voidptr, voidptr, usize) bool = unsafe { nil }
213 can_release_part fn (voidptr, u32) bool = unsafe { nil }
214 allocates_zeros fn (voidptr) bool = unsafe { nil }
215 page_size fn (voidptr) usize = unsafe { nil } // not a constant field because some platforms might have different page sizes depending on configs
216 data voidptr
217}
218
219pub struct Dlmalloc {
220 system_allocator Allocator
221 max_request usize = 4294901657
222mut:
223 // bin maps
224 smallmap u32 // bin map for small bins
225 treemap u32 // bin map for tree bins
226
227 smallbins [66]&Chunk // small bins, it is actually (n_small_bins + 1) * 2
228 treebins [n_tree_bins]&TreeChunk
229 dvsize usize
230 topsize usize
231 dv &Chunk = unsafe { nil }
232 top &Chunk = unsafe { nil }
233 footprint usize
234 max_footprint usize
235 seg Segment
236 trim_check u32
237 least_addr voidptr
238 release_checks usize
239}
240
241// new creates a new instance of `Dlmalloc` with the given system allocator.
242pub fn new(system_allocator Allocator) Dlmalloc {
243 return Dlmalloc{
244 smallmap: 0
245 treemap: 0
246 smallbins: unsafe { [(n_small_bins + 1) * 2]&Chunk{} }
247 treebins: unsafe { [n_tree_bins]&TreeChunk{} }
248 dvsize: 0
249 topsize: 0
250 dv: unsafe { nil }
251 top: unsafe { nil }
252 footprint: 0
253 max_footprint: 0
254 seg: Segment{unsafe { nil }, 0, unsafe { nil }, 0}
255 trim_check: 0
256 least_addr: unsafe { nil }
257 release_checks: 0
258 system_allocator: system_allocator
259 max_request: 4294901657
260 }
261}
262
263@[heap]
264struct Chunk {
265mut:
266 prev_foot usize
267 head usize
268 prev &Chunk = unsafe { nil }
269 next &Chunk = unsafe { nil }
270}
271
272@[heap]
273struct Segment {
274mut:
275 base voidptr
276 size usize
277 next &Segment = unsafe { nil }
278 flags u32
279}
280
281@[heap]
282struct TreeChunk {
283mut:
284 chunk Chunk
285 child [2]voidptr
286 parent voidptr
287 index u32
288}
289
290const pinuse = 1 << 0
291const cinuse = 1 << 1
292const flag4 = 1 << 2
293const inuse = pinuse | cinuse
294const flag_bits = pinuse | cinuse | flag4
295
296fn fencepost_head() usize {
297 return inuse | sizeof(usize)
298}
299
300fn (c &Chunk) size() usize {
301 return c.head & ~flag_bits
302}
303
304fn (c &Chunk) mmapped() bool {
305 return c.head & inuse == 0
306}
307
308fn (c &Chunk) next() &Chunk {
309 mut me := usize(c)
310 me = me + c.size()
311 return unsafe { &Chunk(me) }
312}
313
314fn (c &Chunk) prev() &Chunk {
315 mut me := usize(c)
316 me = me + c.prev_foot
317 return unsafe { &Chunk(me) }
318}
319
320fn (c &Chunk) cinuse() bool {
321 return c.head & cinuse != 0
322}
323
324fn (c &Chunk) pinuse() bool {
325 return c.head & pinuse != 0
326}
327
328fn (mut c Chunk) clear_pinuse() {
329 c.head &= ~pinuse
330}
331
332fn (c &Chunk) inuse() bool {
333 return c.head & inuse != pinuse
334}
335
336fn (mut c Chunk) set_inuse(size usize) {
337 c.head = (c.head & pinuse) | size | cinuse
338 mut next := c.plus_offset(size)
339 next.head |= pinuse
340}
341
342fn (mut c Chunk) set_inuse_and_pinuse(size usize) {
343 c.head = pinuse | size | cinuse
344 mut next := c.plus_offset(size)
345 next.head |= pinuse
346}
347
348fn (mut c Chunk) set_size_and_pinuse_of_inuse_chunk(size usize) {
349 c.head = size | pinuse | cinuse
350}
351
352fn (mut c Chunk) set_size_and_pinuse_of_free_chunk(size usize) {
353 c.head = size | pinuse
354 c.set_foot(size)
355}
356
357fn (mut c Chunk) set_free_with_pinuse(size usize, n_ &Chunk) {
358 mut n := unsafe { n_ }
359 n.clear_pinuse()
360 c.set_size_and_pinuse_of_free_chunk(size)
361}
362
363fn (c &Chunk) set_foot(size usize) {
364 mut next := c.plus_offset(size)
365 next.prev_foot = size
366}
367
368fn (c &Chunk) plus_offset(offset usize) &Chunk {
369 return unsafe { &Chunk((usize(c) + offset)) }
370}
371
372fn (c &Chunk) minus_offset(offset usize) &Chunk {
373 return unsafe { &Chunk(usize(c) - offset) }
374}
375
376fn (c &Chunk) to_mem() voidptr {
377 return voidptr(usize(c) + chunk_mem_offset())
378}
379
380fn chunk_from_mem(mem_ voidptr) &Chunk {
381 mem := usize(mem_)
382 return unsafe { &Chunk((mem - chunk_mem_offset())) }
383}
384
385fn (tree &TreeChunk) leftmost_child() &TreeChunk {
386 left := unsafe { &TreeChunk(tree.child[0]) }
387 if isnil(left) {
388 return tree.child[1]
389 } else {
390 return left
391 }
392}
393
394fn (tree &TreeChunk) chunk() &Chunk {
395 return &tree.chunk
396}
397
398fn (tree &TreeChunk) size(treemap u32) usize {
399 return tree.chunk.head & ~flag_bits
400}
401
402@[unsafe]
403fn (tree &TreeChunk) next() &TreeChunk {
404 unsafe {
405 return &TreeChunk(tree.chunk().next)
406 }
407}
408
409@[unsafe]
410fn (tree &TreeChunk) prev() &TreeChunk {
411 unsafe {
412 return &TreeChunk(tree.chunk().prev)
413 }
414}
415
416const extern = 1 << 0
417
418fn (seg &Segment) is_extern() bool {
419 return seg.flags & extern != 0
420}
421
422fn (seg &Segment) can_release_part(sys_alloc &Allocator) bool {
423 return sys_alloc.can_release_part(sys_alloc.data, seg.flags >> 1)
424}
425
426fn (seg &Segment) sys_flags() u32 {
427 return seg.flags >> 1
428}
429
430fn (seg &Segment) holds(addr voidptr) bool {
431 return seg.base <= addr && addr < seg.top()
432}
433
434fn (seg &Segment) top() voidptr {
435 return voidptr(usize(seg.base) + seg.size)
436}
437
438@[unsafe]
439pub fn (dl &Dlmalloc) calloc_must_clear(ptr voidptr) bool {
440 return !dl.system_allocator.allocates_zeros(dl.system_allocator.data)
441 || !chunk_from_mem(ptr).mmapped()
442}
443
444@[unsafe]
445fn (mut dl Dlmalloc) smallbin_at(idx u32) &Chunk {
446 unsafe {
447 return &Chunk(&dl.smallbins[idx * 2])
448 }
449}
450
451@[unsafe]
452fn (mut dl Dlmalloc) treebin_at(idx u32) &&TreeChunk {
453 return &dl.treebins[idx]
454}
455
456fn (dl &Dlmalloc) compute_tree_index(size usize) u32 {
457 x := size >> tree_bin_shift
458 if x == 0 {
459 return 0
460 } else if x > 0xffff {
461 return n_tree_bins - 1
462 } else {
463 k := sizeof(usize) * 8 - 1 - usize_leading_zeros(x)
464 return u32((k << 1) + ((size >> (k + tree_bin_shift - 1)) & 1))
465 }
466}
467
468@[unsafe]
469fn (mut dl Dlmalloc) unlink_chunk(chunk &Chunk, size usize) {
470 unsafe {
471 if is_small(size) {
472 dl.unlink_small_chunk(chunk, size)
473 } else {
474 dl.unlink_large_chunk(&TreeChunk(chunk))
475 }
476 }
477}
478
479@[unsafe]
480fn (mut dl Dlmalloc) unlink_small_chunk(chunk_ &Chunk, size usize) {
481 mut chunk := unsafe { chunk_ }
482 mut f := chunk.prev
483 mut b := chunk.next
484 idx := small_index(size)
485
486 if voidptr(b) == voidptr(f) {
487 unsafe { dl.clear_smallmap(idx) }
488 } else {
489 f.next = b
490 b.prev = f
491 }
492}
493
494@[unsafe]
495fn (mut dl Dlmalloc) unlink_large_chunk(chunk_ &TreeChunk) {
496 unsafe {
497 mut chunk := chunk_
498 mut xp := &TreeChunk(chunk.parent)
499 mut r := &TreeChunk(nil)
500 if voidptr(chunk.next()) != voidptr(chunk) {
501 mut f := chunk.prev()
502 r = chunk.next()
503 f.chunk.next = r.chunk()
504 r.chunk.prev = f.chunk()
505 } else {
506 mut rp := &&TreeChunk(&chunk.child[1])
507 if isnil(*rp) {
508 rp = &&TreeChunk(&chunk.child[0])
509 }
510
511 r = *rp
512 if !isnil(*rp) {
513 for {
514 mut cp := &&TreeChunk(&rp.child[1])
515 if isnil(*cp) {
516 cp = &&TreeChunk(&rp.child[0])
517 }
518 if isnil(*cp) {
519 break
520 }
521 rp = cp
522 }
523 r = *rp
524 *rp = &TreeChunk(nil)
525 }
526 }
527
528 if isnil(xp) {
529 return
530 }
531
532 mut h := dl.treebin_at(chunk.index)
533 if voidptr(chunk) == voidptr(*h) {
534 *h = r
535 if isnil(r) {
536 dl.clear_treemap(chunk.index)
537 }
538 } else {
539 if xp.child[0] == chunk {
540 xp.child[0] = r
541 } else {
542 xp.child[1] = r
543 }
544 }
545
546 if !isnil(r) {
547 r.parent = xp
548 mut c0 := &TreeChunk(chunk.child[0])
549 if !isnil(c0) {
550 r.child[0] = c0
551 c0.parent = r
552 }
553 mut c1 := &TreeChunk(chunk.child[1])
554 if !isnil(c1) {
555 r.child[1] = c1
556 c1.parent = r
557 }
558 }
559 }
560}
561
562@[unsafe]
563fn (mut dl Dlmalloc) unlink_first_small_chunk(head_ &Chunk, next_ &Chunk, idx u32) {
564 mut next := unsafe { next_ }
565 mut head := unsafe { head_ }
566 // println('Unlink first small')
567 mut ptr := next.prev
568 if voidptr(head) == voidptr(ptr) {
569 unsafe { dl.clear_smallmap(idx) }
570 } else {
571 ptr.next = head
572 head.prev = ptr
573 }
574}
575
576// calloc is the same as `malloc`, except if the allocation succeeds it's guaranteed
577// to point to `size` bytes of zeros.
578@[unsafe]
579pub fn (mut dl Dlmalloc) calloc(size usize) voidptr {
580 unsafe {
581 ptr := dl.malloc(size)
582 if !isnil(ptr) && dl.calloc_must_clear(ptr) {
583 vmemset(ptr, 0, int(size))
584 }
585 return ptr
586 }
587}
588
589// free_ behaves as libc free, but operates within the given space
590@[unsafe]
591pub fn (mut dl Dlmalloc) free_(mem voidptr) {
592 unsafe {
593 // C.VALGRIND_FREELIKE_BLOCK(mem, 0)
594 mut p := chunk_from_mem(mem)
595
596 mut psize := p.size()
597
598 next := p.plus_offset(psize)
599
600 if !p.pinuse() {
601 prevsize := p.prev_foot
602
603 if p.mmapped() {
604 psize += prevsize + mmap_foot_pad()
605 if dl.system_allocator.free_(dl.system_allocator.data,
606 voidptr(usize(p) - prevsize), psize)
607 {
608 dl.footprint -= psize
609 }
610
611 return
612 }
613
614 prev := p.minus_offset(prevsize)
615 psize += prevsize
616 p = prev
617 if voidptr(p) != voidptr(dl.dv) {
618 dl.unlink_chunk(p, prevsize)
619 } else if (next.head & inuse) == inuse {
620 dl.dvsize = psize
621 p.set_free_with_pinuse(psize, next)
622
623 return
624 }
625 }
626 // consolidate forward if we can
627 if !next.cinuse() {
628 if voidptr(next) == voidptr(dl.top) {
629 dl.topsize += psize
630 p.head = 0
631
632 tsize := dl.topsize
633 dl.top = p
634 p.head = tsize | pinuse
635 if voidptr(p) == voidptr(dl.dv) {
636 dl.dv = nil
637 dl.dvsize = 0
638 }
639
640 if dl.should_trim(tsize) {
641 dl.sys_trim(0)
642 }
643
644 return
645 } else if voidptr(next) == voidptr(dl.dv) {
646 dl.dvsize += psize
647 dsize := dl.dvsize
648 dl.dv = p
649 p.set_size_and_pinuse_of_free_chunk(dsize)
650
651 return
652 } else {
653 nsize := next.size()
654 psize += nsize
655 dl.unlink_chunk(next, nsize)
656 p.set_size_and_pinuse_of_free_chunk(psize)
657
658 if voidptr(p) == voidptr(dl.dv) {
659 dl.dvsize = psize
660 return
661 }
662 }
663 } else {
664 p.set_free_with_pinuse(psize, next)
665 }
666
667 if is_small(psize) {
668 dl.insert_small_chunk(p, psize)
669 } else {
670 dl.insert_large_chunk(&TreeChunk(p), psize)
671 dl.release_checks -= 1
672 if dl.release_checks == 0 {
673 dl.release_unused_segments()
674 }
675 }
676 }
677}
678
679fn (dl &Dlmalloc) should_trim(size usize) bool {
680 return size > dl.trim_check
681}
682
683@[unsafe]
684fn (mut dl Dlmalloc) sys_trim(pad_ usize) bool {
685 unsafe {
686 mut pad := pad_
687 mut released := usize(0)
688 if pad < dl.max_request && !isnil(dl.top) {
689 pad += top_foot_size()
690 if dl.topsize > pad {
691 unit := usize(default_granularity())
692 extra := ((dl.topsize - pad + unit - 1) / unit - 1) * unit
693 mut sp := dl.segment_holding(dl.top)
694
695 if !sp.is_extern() {
696 if sp.can_release_part(&dl.system_allocator) {
697 if sp.size >= extra && !dl.has_segment_link(sp) {
698 newsize := sp.size - extra
699 if dl.system_allocator.free_part(dl.system_allocator.data, sp.base,
700 sp.size, newsize)
701 {
702 released = extra
703 }
704 }
705 }
706 }
707
708 if released != 0 {
709 sp.size -= released
710 dl.footprint -= released
711 top := dl.top
712 topsize := dl.topsize - released
713 dl.init_top(top, topsize)
714 }
715 }
716
717 released += dl.release_unused_segments()
718
719 if released == 0 && dl.topsize > dl.trim_check {
720 dl.trim_check = 1 << 31
721 }
722 }
723 return released != 0
724 }
725}
726
727@[unsafe]
728fn (mut dl Dlmalloc) release_unused_segments() usize {
729 unsafe {
730 mut released := usize(0)
731 mut nsegs := usize(0)
732 mut pred := &dl.seg
733 mut sp := pred.next
734 for !isnil(sp) {
735 base := sp.base
736 size := sp.size
737 next := sp.next
738
739 nsegs += 1
740
741 if sp.can_release_part(&dl.system_allocator) && !sp.is_extern() {
742 mut p := align_as_chunk(base)
743 psize := p.size()
744 chunk_top := voidptr(usize(p) + psize)
745 top := voidptr(usize(base) + (size - top_foot_size()))
746 if !p.inuse() && chunk_top >= top {
747 mut tp := &TreeChunk(p)
748 if voidptr(p) == voidptr(dl.dv) {
749 dl.dv = nil
750 dl.dvsize = 0
751 } else {
752 dl.unlink_large_chunk(tp)
753 }
754
755 if dl.system_allocator.free_(dl.system_allocator.data, base, size) {
756 released += size
757 dl.footprint -= size
758 sp = pred
759 sp.next = next
760 } else {
761 // back out if we can't unmap
762 dl.insert_large_chunk(tp, psize)
763 }
764 }
765 }
766 pred = sp
767 sp = next
768 }
769 dl.release_checks = if nsegs > max_release_check_rate {
770 nsegs
771 } else {
772 max_release_check_rate
773 }
774 return released
775 }
776}
777
778@[unsafe]
779fn (dl &Dlmalloc) has_segment_link(ptr &Segment) bool {
780 mut sp := &dl.seg
781 for !isnil(sp) {
782 if ptr.holds(sp) {
783 return true
784 }
785 sp = sp.next
786 }
787 return false
788}
789
790@[unsafe]
791fn (mut dl Dlmalloc) replace_dv(chunk &Chunk, size usize) {
792 dvs := dl.dvsize
793 if dvs != 0 {
794 dv := dl.dv
795 unsafe {
796 dl.insert_small_chunk(dv, dvs)
797 }
798 }
799 dl.dvsize = size
800 dl.dv = chunk
801}
802
803@[unsafe]
804fn (mut dl Dlmalloc) insert_chunk(chunk &Chunk, size usize) {
805 unsafe {
806 if is_small(size) {
807 dl.insert_small_chunk(chunk, size)
808 } else {
809 dl.insert_large_chunk(&TreeChunk(chunk), size)
810 }
811 }
812}
813
814@[unsafe]
815fn (mut dl Dlmalloc) insert_small_chunk(chunk_ &Chunk, size usize) {
816 mut chunk := unsafe { chunk_ }
817 idx := small_index(size)
818 unsafe {
819 mut head := dl.smallbin_at(idx)
820 mut f := head
821 if !dl.smallmap_is_marked(idx) {
822 dl.mark_smallmap(idx)
823 } else {
824 f = head.prev
825 }
826
827 assert !isnil(f)
828 assert !isnil(head)
829 head.prev = chunk
830 f.next = chunk
831 chunk.prev = f
832 chunk.next = head
833 }
834}
835
836@[unsafe]
837fn (mut dl Dlmalloc) insert_large_chunk(chunk_ &TreeChunk, size usize) {
838 unsafe {
839 mut chunk := chunk_
840 idx := dl.compute_tree_index(size)
841 mut h := dl.treebin_at(idx)
842
843 chunk.index = idx
844 chunk.child[0] = nil
845 chunk.child[1] = nil
846
847 mut chunkc := chunk.chunk()
848 if !dl.treemap_is_marked(idx) {
849 dl.mark_treemap(idx)
850 *h = chunk
851 chunk.parent = voidptr(h)
852 assert !isnil(chunkc)
853 chunkc.prev = chunkc
854 chunkc.next = chunkc
855 } else {
856 mut t := *h
857 mut k := size << leftshift_for_tree_index(idx)
858 for {
859 if t.chunk().size() != size {
860 c_ := &t.child[int((k >> sizeof(usize) * 8 - 1) & 1)]
861 mut c := &&TreeChunk(c_)
862 k <<= 1
863 if !isnil(c) {
864 t = *c
865 } else {
866 *c = chunk
867 chunk.parent = t
868 chunkc.next = chunkc
869 chunkc.prev = chunkc
870 break
871 }
872 } else {
873 tc := t.chunk()
874 f := tc.prev
875 f.next = chunkc
876 assert !isnil(chunkc)
877 tc.prev = chunkc
878 chunkc.prev = f
879 chunkc.next = tc
880 chunk.parent = nil
881 break
882 }
883 }
884 }
885 }
886}
887
888@[unsafe]
889fn (mut dl Dlmalloc) clear_smallmap(idx u32) {
890 dl.smallmap &= ~(1 << idx)
891}
892
893@[unsafe]
894fn (mut dl Dlmalloc) mark_smallmap(idx u32) {
895 dl.smallmap |= 1 << idx
896}
897
898@[unsafe]
899fn (mut dl Dlmalloc) smallmap_is_marked(idx u32) bool {
900 return (dl.smallmap & (1 << idx)) != 0
901}
902
903@[unsafe]
904fn (mut dl Dlmalloc) clear_treemap(idx u32) {
905 dl.treemap &= ~(1 << idx)
906}
907
908@[unsafe]
909fn (mut dl Dlmalloc) mark_treemap(idx u32) {
910 dl.treemap |= 1 << idx
911}
912
913@[unsafe]
914fn (mut dl Dlmalloc) treemap_is_marked(idx u32) bool {
915 return dl.treemap & (1 << idx) != 0
916}
917
918// malloc allocates a block of memory of the given size.
919pub fn (mut dl Dlmalloc) malloc(size usize) voidptr {
920 unsafe {
921 p := dl.malloc_real(size)
922 if !isnil(p) {
923 // C.VALGRIND_MALLOCLIKE_BLOCK(p, size, 0,false)
924 }
925 return p
926 }
927}
928
929// malloc_real behaves as libc malloc, but operates within the given space
930@[unsafe]
931fn (mut dl Dlmalloc) malloc_real(size usize) voidptr {
932 mut nb := usize(0)
933 unsafe {
934 if size <= max_small_request() {
935 nb = request_2_size(size)
936 mut idx := small_index(nb)
937 smallbits := dl.smallmap >> idx
938 if smallbits & 0b11 != 0 {
939 idx += ~smallbits & 1
940
941 b := dl.smallbin_at(idx)
942 mut p := b.prev
943 smallsize := small_index2size(idx)
944
945 dl.unlink_first_small_chunk(b, p, idx)
946
947 p.set_inuse_and_pinuse(smallsize)
948
949 ret := p.to_mem()
950
951 return ret
952 }
953
954 if nb > dl.dvsize {
955 // if there's some other bin with some memory, then we just use
956 // the next smallest bin
957
958 // todo(playXE): Find out why in the world this part of code does not work in
959 // some programs (esp. x.json2). Theoretically disabling this path just
960 // makes fragmentation a little worse but nothing really bad should happen
961 if false && smallbits != 0 {
962 leftbits := (smallbits << idx) & left_bits(1 << idx)
963 leastbit := least_bit(leftbits)
964 i := u32(bits.trailing_zeros_32(leastbit))
965 mut b := dl.smallbin_at(i)
966 mut p := b.prev
967 dl.unlink_first_small_chunk(b, p, i)
968 smallsize := small_index2size(i)
969 rsize := smallsize - nb
970 if sizeof(usize) != 4 && rsize < min_chunk_size() {
971 p.set_inuse_and_pinuse(smallsize)
972 } else {
973 p.set_size_and_pinuse_of_inuse_chunk(nb)
974 mut r := p.plus_offset(nb)
975 r.set_size_and_pinuse_of_free_chunk(size)
976 dl.replace_dv(r, rsize)
977 }
978
979 ret := p.to_mem()
980
981 return ret
982 } else if dl.treemap != 0 {
983 mem := dl.tmalloc_small(nb)
984 if !isnil(mem) {
985 return mem
986 }
987 }
988 }
989 } else if size >= dl.max_request {
990 return nil
991 } else {
992 nb = pad_request(size)
993 if dl.treemap != 0 {
994 mem := dl.tmalloc_large(nb)
995 if !isnil(mem) {
996 return mem
997 }
998 }
999 }
1000
1001 // use the `dv` node if we can, splitting it if necessary or otherwise
1002 // exhausting the entire chunk
1003 if nb <= dl.dvsize {
1004 rsize := dl.dvsize - nb
1005 mut p := dl.dv
1006 if rsize >= min_chunk_size() {
1007 dl.dv = p.plus_offset(nb)
1008 dl.dvsize = rsize
1009 mut r := dl.dv
1010 r.set_size_and_pinuse_of_free_chunk(rsize)
1011 p.set_size_and_pinuse_of_inuse_chunk(nb)
1012 } else {
1013 dvs := dl.dvsize
1014 dl.dvsize = 0
1015 dl.dv = nil
1016 p.set_inuse_and_pinuse(dvs)
1017 }
1018 ret := p.to_mem()
1019
1020 return ret
1021 }
1022 // Split the top node if we can
1023 if nb < dl.topsize {
1024 dl.topsize -= nb
1025 rsize := dl.topsize
1026 mut p := dl.top
1027 dl.top = p.plus_offset(nb)
1028 mut r := dl.top
1029 r.head = rsize | pinuse
1030 p.set_size_and_pinuse_of_inuse_chunk(nb)
1031 ret := p.to_mem()
1032
1033 return ret
1034 }
1035
1036 return dl.sys_alloc(nb)
1037 }
1038}
1039
1040@[unsafe]
1041fn (mut dl Dlmalloc) init_bins() {
1042 unsafe {
1043 for i in 0 .. n_small_bins {
1044 mut bin := dl.smallbin_at(i)
1045 bin.prev = bin
1046 bin.next = bin
1047 }
1048 }
1049}
1050
1051@[unsafe]
1052fn (mut dl Dlmalloc) init_top(ptr &Chunk, size_ usize) {
1053 pmem := ptr.to_mem()
1054 offset := align_offset_usize(usize(pmem))
1055 mut p := ptr.plus_offset(offset)
1056
1057 size := size_ - offset
1058 dl.top = p
1059 dl.topsize = size
1060 // C.VALGRIND_MAKE_MEM_UNDEFINED(p.plus_offset(sizeof(usize)),sizeof(usize))
1061 p.head = size | pinuse
1062 // C.VALGRIND_MAKE_MEM_UNDEFINED(p.plus_offset(size + sizeof(usize)),sizeof(usize))
1063 p.plus_offset(size).head = top_foot_size()
1064 dl.trim_check = u32(default_trim_threshold())
1065}
1066
1067@[unsafe]
1068fn (mut dl Dlmalloc) sys_alloc(size usize) voidptr {
1069 page_size := dl.system_allocator.page_size(dl.system_allocator.data)
1070 asize := align_up(align_up(size + top_foot_size() + malloc_alignment(), default_granularity()),
1071 page_size)
1072 unsafe {
1073 alloc := dl.system_allocator.alloc
1074 tbase, mut tsize, flags := alloc(dl.system_allocator.data, asize)
1075
1076 if isnil(tbase) {
1077 return tbase
1078 }
1079
1080 dl.footprint += tsize
1081 dl.max_footprint = if dl.max_footprint > dl.footprint {
1082 dl.max_footprint
1083 } else {
1084 dl.footprint
1085 }
1086 if isnil(dl.top) {
1087 if isnil(dl.least_addr) || tbase < dl.least_addr {
1088 dl.least_addr = tbase
1089 }
1090 dl.seg.base = tbase
1091 dl.seg.size = tsize
1092 dl.seg.flags = flags
1093 dl.release_checks = max_release_check_rate
1094 dl.init_bins()
1095 tsize_ := tsize - top_foot_size()
1096 dl.init_top(&Chunk(tbase), tsize_)
1097 } else {
1098 mut sp := &dl.seg
1099 for !isnil(sp) && voidptr(tbase) != voidptr(sp.top()) {
1100 sp = sp.next
1101 }
1102
1103 if !isnil(sp) && !sp.is_extern() && sp.sys_flags() == flags && sp.holds(dl.top) {
1104 sp.size += tsize
1105 ptr := dl.top
1106 size_ := dl.topsize + tsize
1107 dl.init_top(ptr, size_)
1108 } else {
1109 if tbase < dl.least_addr {
1110 dl.least_addr = tbase
1111 } else {
1112 dl.least_addr = dl.least_addr
1113 }
1114 sp = &dl.seg
1115 for !isnil(sp) && sp.base != voidptr(usize(tbase) + tsize) {
1116 sp = sp.next
1117 }
1118
1119 if !isnil(sp) && !sp.is_extern() && sp.sys_flags() == flags {
1120 oldbase := sp.base
1121 sp.base = tbase
1122 sp.size = tsize
1123 return dl.prepend_alloc(tbase, oldbase, size)
1124 } else {
1125 dl.add_segment(tbase, tsize, flags)
1126 }
1127 }
1128 }
1129
1130 if size < dl.topsize {
1131 dl.topsize -= size
1132 rsize := dl.topsize
1133 mut p := dl.top
1134 dl.top = p.plus_offset(size)
1135 mut r := dl.top
1136 r.head = rsize | pinuse
1137 p.set_size_and_pinuse_of_inuse_chunk(size)
1138 ret := p.to_mem()
1139
1140 return ret
1141 }
1142 }
1143 return unsafe { nil }
1144}
1145
1146@[unsafe]
1147fn (mut dl Dlmalloc) tmalloc_small(size usize) voidptr {
1148 unsafe {
1149 leastbit := least_bit(dl.treemap)
1150 i := bits.trailing_zeros_32(leastbit)
1151 mut v := *dl.treebin_at(u32(i))
1152 mut t := v
1153 mut rsize := t.size(dl.treemap)
1154 for {
1155 t = t.leftmost_child()
1156 if isnil(t) {
1157 break
1158 }
1159
1160 trem := t.chunk().size() - size
1161 if trem < rsize {
1162 rsize = trem
1163 v = t
1164 }
1165 }
1166
1167 mut vc := v.chunk()
1168 r := &TreeChunk(vc.plus_offset(size))
1169 dl.unlink_large_chunk(v)
1170 if rsize < min_chunk_size() {
1171 vc.set_inuse_and_pinuse(rsize + size)
1172 } else {
1173 mut rc := r.chunk()
1174 vc.set_size_and_pinuse_of_inuse_chunk(size)
1175 rc.set_size_and_pinuse_of_free_chunk(rsize)
1176 dl.replace_dv(rc, rsize)
1177 }
1178
1179 return vc.to_mem()
1180 }
1181}
1182
1183@[unsafe]
1184fn (mut dl Dlmalloc) tmalloc_large(size usize) voidptr {
1185 unsafe {
1186 mut v := &TreeChunk(nil)
1187 mut rsize := ~size + 1
1188 idx := dl.compute_tree_index(size)
1189 mut t := *dl.treebin_at(idx)
1190 if !isnil(t) {
1191 mut sizebits := size << leftshift_for_tree_index(idx)
1192 mut rst := voidptr(u64(0))
1193 for {
1194 csize := t.chunk().size()
1195 if csize >= size && csize - size < rsize {
1196 v = t
1197 rsize = csize - size
1198 if rsize == 0 {
1199 break
1200 }
1201 }
1202
1203 rt := t.child[1]
1204 t = t.child[int((sizebits >> (sizeof(usize) * 8 - 1)) & 1)]
1205 if !isnil(rt) && voidptr(rt) != voidptr(t) {
1206 rst = rt
1207 }
1208 if isnil(t) {
1209 t = rst
1210 break
1211 }
1212 sizebits <<= 1
1213 }
1214 }
1215
1216 if isnil(t) && isnil(v) {
1217 leftbits := left_bits(1 << idx) & dl.treemap
1218 if leftbits != 0 {
1219 leastbit := least_bit(leftbits)
1220 i := bits.trailing_zeros_32(leastbit)
1221 t = *dl.treebin_at(u32(i))
1222 }
1223 }
1224 // Find the smallest of this tree or subtree
1225 for !isnil(t) {
1226 csize := t.chunk().size()
1227 if csize >= size && csize - size < rsize {
1228 rsize = csize - size
1229 v = t
1230 }
1231 t = t.leftmost_child()
1232 }
1233
1234 if isnil(v) || (dl.dvsize >= size && !(rsize < dl.dvsize - size)) {
1235 return nil
1236 }
1237
1238 mut vc := v.chunk()
1239 mut r := vc.plus_offset(size)
1240 dl.unlink_large_chunk(v)
1241 if rsize < min_chunk_size() {
1242 vc.set_inuse_and_pinuse(rsize + size)
1243 } else {
1244 vc.set_size_and_pinuse_of_inuse_chunk(size)
1245 r.set_size_and_pinuse_of_free_chunk(rsize)
1246 dl.insert_chunk(r, rsize)
1247 }
1248
1249 return vc.to_mem()
1250 }
1251}
1252
1253@[unsafe]
1254fn (mut dl Dlmalloc) prepend_alloc(newbase voidptr, oldbase voidptr, size usize) voidptr {
1255 unsafe {
1256 mut p := align_as_chunk(newbase)
1257 mut oldfirst := align_as_chunk(oldbase)
1258 psize := usize(oldfirst) - usize(p)
1259 mut q := p.plus_offset(size)
1260 mut qsize := psize - size
1261 // C.VALGRIND_MAKE_MEM_UNDEFINED(p.plus_offset(sizeof(usize)),size)
1262 p.set_size_and_pinuse_of_inuse_chunk(size)
1263
1264 if qsize >= sizeof(TreeChunk) {
1265 // C.VALGRIND_MAKE_MEM_UNDEFINED(q, sizeof(TreeChunk))
1266 } else {
1267 // C.VALGRIND_MAKE_MEM_UNDEFINED(q,sizeof(Chunk))
1268 }
1269 // C.VALGRIND_MAKE_MEM_UNDEFINED(q.plus_offset(qsize),sizeof(usize))
1270
1271 if voidptr(oldfirst) == voidptr(dl.top) {
1272 dl.topsize += qsize
1273 tsize := dl.topsize
1274 dl.top = q
1275 q.head = tsize | pinuse
1276 } else if voidptr(oldfirst) == voidptr(dl.dv) {
1277 dl.dvsize += qsize
1278 dsize := dl.dvsize
1279 dl.dv = q
1280 q.set_size_and_pinuse_of_free_chunk(dsize)
1281 } else {
1282 if !oldfirst.inuse() {
1283 nsize := oldfirst.size()
1284 dl.unlink_chunk(oldfirst, nsize)
1285 oldfirst = oldfirst.plus_offset(nsize)
1286 qsize += nsize
1287 }
1288 q.set_free_with_pinuse(qsize, oldfirst)
1289 dl.insert_chunk(q, qsize)
1290 }
1291
1292 ret := p.to_mem()
1293 return ret
1294 }
1295}
1296
1297@[unsafe]
1298fn (mut dl Dlmalloc) add_segment(tbase voidptr, tsize usize, flags u32) {
1299 // TODO: what in the world is this function doing????
1300 unsafe {
1301 old_top := dl.top
1302 mut oldsp := dl.segment_holding(old_top)
1303 old_end := oldsp.top()
1304 ssize := pad_request(sizeof(Segment))
1305 mut offset := ssize + sizeof(usize) * 4 + malloc_alignment() - 1
1306 rawsp := voidptr(usize(old_end) - offset)
1307 pmem := (&Chunk(rawsp)).to_mem()
1308 offset = align_offset_usize(usize(pmem))
1309 asp := voidptr(usize(rawsp) + offset)
1310 csp := if asp < voidptr(usize(old_top) + min_chunk_size()) { old_top } else { asp }
1311 mut sp := &Chunk(csp)
1312 mut ss := &Segment(sp.to_mem())
1313 mut tnext := sp.plus_offset(ssize)
1314 mut p := tnext
1315 mut nfences := 0
1316
1317 size := tsize - top_foot_size()
1318 dl.init_top(&Chunk(tbase), size)
1319
1320 sp.set_size_and_pinuse_of_inuse_chunk(ssize)
1321 *ss = dl.seg
1322 dl.seg.base = tbase
1323 dl.seg.size = tsize
1324 dl.seg.flags = flags
1325 dl.seg.next = ss
1326
1327 for {
1328 nextp := p.plus_offset(sizeof(usize))
1329 p.head = fencepost_head()
1330 nfences += 1
1331 if nextp.head < old_end {
1332 p = nextp
1333 } else {
1334 break
1335 }
1336 }
1337 // TODO: why 2?
1338 assert nfences >= 2
1339 if voidptr(csp) != voidptr(old_top) {
1340 mut q := &Chunk(old_top)
1341 psize := usize(csp) - usize(old_top)
1342 tn := q.plus_offset(psize)
1343 q.set_free_with_pinuse(psize, tn)
1344
1345 dl.insert_chunk(q, psize)
1346 }
1347 }
1348}
1349
1350@[unsafe]
1351fn (mut dl Dlmalloc) segment_holding(ptr voidptr) &Segment {
1352 mut sp := &dl.seg
1353 for !isnil(sp) {
1354 if sp.base <= ptr && ptr < sp.top() {
1355 return sp
1356 }
1357 sp = sp.next
1358 }
1359 return &Segment(unsafe { nil })
1360}
1361
1362// realloc behaves as libc realloc, but operates within the given space
1363@[unsafe]
1364pub fn (mut dl Dlmalloc) realloc(oldmem voidptr, bytes usize) voidptr {
1365 if bytes >= dl.max_request {
1366 return unsafe { nil }
1367 }
1368 unsafe {
1369 nb := request_2_size(bytes)
1370 mut oldp := chunk_from_mem(oldmem)
1371 newp := dl.try_realloc_chunk(oldp, nb, true)
1372 if !isnil(newp) {
1373 return newp.to_mem()
1374 }
1375
1376 ptr := dl.malloc(bytes)
1377 if !isnil(ptr) {
1378 oc := oldp.size() - overhead_for(oldp)
1379 copy_bytes := if oc < bytes { oc } else { bytes }
1380 vmemcpy(ptr, oldmem, int(copy_bytes))
1381 }
1382
1383 return ptr
1384 }
1385}
1386
1387// memaligns allocates memory aligned to `alignment_`. Only call this with power-of-two alignment
1388// and alignment > dlmalloc.malloc_alignment
1389@[unsafe]
1390pub fn (mut dl Dlmalloc) memalign(alignment_ usize, bytes usize) voidptr {
1391 mut alignment := alignment_
1392 if alignment < min_chunk_size() {
1393 alignment = min_chunk_size()
1394 }
1395
1396 if bytes >= max_request() - alignment {
1397 return unsafe { nil }
1398 }
1399 unsafe {
1400 nb := request_2_size(bytes)
1401 req := nb + alignment + min_chunk_size() - chunk_overhead()
1402 mem := dl.malloc(req)
1403 if isnil(mem) {
1404 return mem
1405 }
1406
1407 mut p := chunk_from_mem(mem)
1408 if usize(mem) & (alignment - 1) != 0 {
1409 // Here we find an aligned sopt inside the chunk. Since we need to
1410 // give back leading space in a chunk of at least `min_chunk_size`,
1411 // if the first calculation places us at a spot with less than
1412 // `min_chunk_size` leader we can move to the next aligned spot.
1413 // we've allocated enough total room so that this is always possible
1414 br_ := (usize(mem) + alignment - 1) & (~alignment + 1)
1415 br := chunk_from_mem(voidptr(br_))
1416 mut pos := voidptr(u64(0))
1417 if usize(br) - usize(p) > min_chunk_size() {
1418 pos = voidptr(br)
1419 } else {
1420 pos = voidptr(usize(br) + alignment)
1421 }
1422
1423 mut newp := &Chunk(pos)
1424 leadsize := usize(pos) - usize(p)
1425 newsize := p.size() - leadsize
1426
1427 if p.mmapped() {
1428 newp.prev_foot = p.prev_foot + leadsize
1429 newp.head = newsize
1430 } else {
1431 newp.set_inuse(newsize)
1432 p.set_inuse(leadsize)
1433 dl.dispose_chunk(p, leadsize)
1434 }
1435 p = newp
1436 }
1437
1438 if !p.mmapped() {
1439 size := p.size()
1440 if size > nb + min_chunk_size() {
1441 remainder_size := size - nb
1442 mut remainder := p.plus_offset(nb)
1443 p.set_inuse(nb)
1444 remainder.set_inuse(remainder_size)
1445 dl.dispose_chunk(remainder, remainder_size)
1446 }
1447 }
1448
1449 // C.VALGRIND_MALLOCLIKE_BLOCK(p.to_mem(), bytes, 0, false)
1450 return p.to_mem()
1451 }
1452}
1453
1454@[unsafe]
1455fn (mut dl Dlmalloc) try_realloc_chunk(p_ &Chunk, nb usize, can_move bool) &Chunk {
1456 unsafe {
1457 mut p := p_
1458 oldsize := p.size()
1459 mut next := p.plus_offset(oldsize)
1460 if p.mmapped() {
1461 return dl.mmap_resize(p, nb, can_move)
1462 } else if oldsize >= nb {
1463 rsize := oldsize - nb
1464 if rsize >= min_chunk_size() {
1465 mut r := p.plus_offset(nb)
1466 p.set_inuse(nb)
1467 r.set_inuse(rsize)
1468 dl.dispose_chunk(r, rsize)
1469 }
1470 return p
1471 } else if voidptr(next) == voidptr(dl.top) {
1472 if oldsize + dl.topsize <= nb {
1473 return nil
1474 }
1475
1476 newsize := oldsize + dl.topsize
1477 newtopsize := newsize - nb
1478 mut newtop := p.plus_offset(nb)
1479 p.set_inuse(nb)
1480 newtop.head = newtopsize | pinuse
1481 dl.top = newtop
1482 dl.topsize = newtopsize
1483 return p
1484 } else if voidptr(next) == voidptr(dl.dv) {
1485 dvs := dl.dvsize
1486 if oldsize + dvs < nb {
1487 return nil
1488 }
1489
1490 dsize := oldsize + dvs - nb
1491 if dsize >= min_chunk_size() {
1492 mut r := p.plus_offset(nb)
1493 mut n := r.plus_offset(dsize)
1494 p.set_inuse(nb)
1495 r.set_size_and_pinuse_of_free_chunk(dsize)
1496 n.clear_pinuse()
1497 dl.dvsize = dsize
1498 dl.dv = r
1499 } else {
1500 newsize := oldsize + dvs
1501 p.set_inuse(newsize)
1502 dl.dvsize = 0
1503 dl.dv = nil
1504 }
1505 return p
1506 } else if !next.cinuse() {
1507 nextsize := next.size()
1508 if oldsize + nextsize < nb {
1509 return nil
1510 }
1511 rsize := oldsize + nextsize - nb
1512 dl.unlink_chunk(next, nextsize)
1513 if rsize < min_chunk_size() {
1514 newsize := oldsize + nextsize
1515 p.set_inuse(newsize)
1516 } else {
1517 r := p.plus_offset(nb)
1518 p.set_inuse(nb)
1519 r.set_inuse(rsize)
1520 dl.dispose_chunk(r, rsize)
1521 }
1522 return p
1523 } else {
1524 return nil
1525 }
1526 }
1527}
1528
1529@[unsafe]
1530fn (mut dl Dlmalloc) mmap_resize(oldp_ &Chunk, nb usize, can_move bool) &Chunk {
1531 mut oldp := unsafe { oldp_ }
1532 oldsize := oldp.size()
1533 if is_small(nb) {
1534 return unsafe { nil }
1535 }
1536 // Keep the old chunk if it's big enough but not too big
1537 if oldsize >= nb + sizeof(usize) && (oldsize - nb) <= (default_granularity() << 1) {
1538 return oldp
1539 }
1540
1541 offset := oldp.prev_foot
1542 oldmmsize := oldsize + offset + mmap_foot_pad()
1543 newmmsize := dl.mmap_align(nb + 6 * sizeof(usize) + malloc_alignment() - 1)
1544
1545 ptr := dl.system_allocator.remap(dl.system_allocator.data, voidptr(usize(oldp) - offset),
1546 oldmmsize, newmmsize, can_move)
1547 if isnil(ptr) {
1548 return unsafe { nil }
1549 }
1550
1551 mut newp := unsafe { &Chunk(voidptr(usize(ptr) + offset)) }
1552 psize := newmmsize - offset - mmap_foot_pad()
1553 newp.head = psize
1554 newp.plus_offset(psize).head = fencepost_head()
1555 newp.plus_offset(psize + sizeof(usize)).head = 0
1556 if ptr < dl.least_addr {
1557 dl.least_addr = ptr
1558 }
1559 dl.footprint = dl.footprint + newmmsize - oldmmsize
1560 if dl.footprint > dl.max_footprint {
1561 dl.max_footprint = dl.footprint
1562 }
1563 return newp
1564}
1565
1566fn (dl &Dlmalloc) mmap_align(a usize) usize {
1567 return align_up(a, dl.system_allocator.page_size(dl.system_allocator.data))
1568}
1569
1570@[unsafe]
1571fn (mut dl Dlmalloc) dispose_chunk(p_ &Chunk, psize_ usize) {
1572 mut p := unsafe { p_ }
1573 mut psize := psize_
1574 unsafe {
1575 mut next := p.plus_offset(psize)
1576 if !p.pinuse() {
1577 prevsize := p.prev_foot
1578 if p.mmapped() {
1579 psize += prevsize + mmap_foot_pad()
1580
1581 if dl.system_allocator.free_(dl.system_allocator.data,
1582 voidptr(usize(p) - prevsize), psize)
1583 {
1584 dl.footprint -= psize
1585 }
1586 return
1587 }
1588
1589 prev := p.minus_offset(prevsize)
1590 psize += prevsize
1591 p = prev
1592 if voidptr(p) != voidptr(dl.dv) {
1593 dl.unlink_chunk(p, prevsize)
1594 } else if next.head & inuse == inuse {
1595 dl.dvsize = psize
1596 p.set_free_with_pinuse(psize, next)
1597 return
1598 }
1599 }
1600
1601 if !next.cinuse() {
1602 if voidptr(next) == voidptr(dl.top) {
1603 dl.topsize += psize
1604 tsize := dl.topsize
1605 dl.top = p
1606 p.head = tsize | pinuse
1607 if voidptr(p) == voidptr(dl.dv) {
1608 dl.dv = nil
1609 dl.dvsize = 0
1610 }
1611 return
1612 } else if voidptr(next) == voidptr(dl.dv) {
1613 dl.dvsize += psize
1614 dvsize := dl.dvsize
1615 dl.dv = p
1616 p.set_size_and_pinuse_of_free_chunk(dvsize)
1617 return
1618 } else {
1619 nsize := next.size()
1620 psize += nsize
1621 dl.unlink_chunk(next, nsize)
1622 p.set_size_and_pinuse_of_free_chunk(psize)
1623 if voidptr(p) == voidptr(dl.dv) {
1624 dl.dvsize = psize
1625 return
1626 }
1627 }
1628 } else {
1629 p.set_free_with_pinuse(psize, next)
1630 }
1631 dl.insert_chunk(p, psize)
1632 }
1633}
1634