1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1986 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* file: malloc.c
* description:
* Yet another memory allocator, this one based on a method
* described in C.J. Stephenson, "Fast Fits"
*
* The basic data structure is a "Cartesian" binary tree, in which
* nodes are ordered by ascending addresses (thus minimizing free
* list insertion time) and block sizes decrease with depth in the
* tree (thus minimizing search time for a block of a given size).
*
* In other words: for any node s, let D(s) denote the set of
* descendents of s; for all x in D(left(s)) and all y in
* D(right(s)), we have:
*
* a. addr(x) < addr(s) < addr(y)
* b. len(x) <= len(s) >= len(y)
*/
#include "mallint.h"
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
/* system interface */
extern char *sbrk();
extern int getpagesize();
static int nbpg = 0; /* set by calling getpagesize() */
static bool morecore(uint); /* get more memory into free space */
#ifdef S5EMUL
#define ptr_t void * /* ANSI C says these are voids */
#define free_t void /* ANSI says void free(ptr_t ptr) */
#define free_return(x) return
#else
#define ptr_t char * /* BSD still (4.3) wants char*'s */
#define free_t int /* BSD says int free(ptr_t ptr) */
#define free_return(x) return(x)
#endif
/* SystemV-compatible information structure */
#define INIT_MXFAST 0
#define INIT_NLBLKS 100
#define INIT_GRAIN ALIGNSIZ
struct mallinfo __mallinfo = {
0,0,0,0,0,0,0,0,0,0, /* basic info */
INIT_MXFAST, INIT_NLBLKS, INIT_GRAIN, /* mallopt options */
0,0,0
};
/* heap data structures */
Freehdr _root = NIL; /* root of free space list */
char *_lbound = NULL; /* lower bound of heap */
char *_ubound = NULL; /* upper bound of heap */
/* free header list management */
static Freehdr getfreehdr(void);
static void putfreehdr(Freehdr);
static Freehdr freehdrptr = NIL; /* ptr to block of available headers */
static int nfreehdrs = 0; /* # of headers in current block */
static Freehdr freehdrlist = NIL; /* List of available headers */
/* error checking */
static void error(char *, ...);
/* sets errno; prints msg and aborts if DEBUG is on */
static int reclaim(Dblk, uint, int);
#ifdef DEBUG /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
int malloc_debug(int);
int malloc_verify(void);
static int debug_level = 1;
/*
* A block with a negative size, a size that is not a multiple
* of ALIGNSIZ, a size greater than the current extent of the
* heap, or a size which extends beyond the end of the heap is
* considered bad.
*/
#define badblksize(p,size)\
( (size) < SMALLEST_BLK \
|| (size) & (ALIGNSIZ-1) \
|| (size) > heapsize() \
|| ((char*)(p))+(size) > _ubound )
#else /* !DEBUG ================================================= */
#define malloc_debug(level) 0
#define malloc_verify() 1
#define debug_level 0
#define badblksize(p,size) 0
#endif /* !DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
/*
* insert (newblk, len)
* Inserts a new node in the free space tree, placing it
* in the correct position with respect to the existing nodes.
*
* algorithm:
* Starting from the root, a binary search is made for the new
* node. If this search were allowed to continue, it would
* eventually fail (since there cannot already be a node at the
* given address); but in fact it stops when it reaches a node in
* the tree which has a length less than that of the new node (or
* when it reaches a null tree pointer).
*
* The new node is then inserted at the root of the subtree for
* which the shorter node forms the old root (or in place of the
* null pointer).
*
* Arguments
* newblk: Ptr to the block to insert
* len: Length of new node
*/
static void
insert(Dblk newblk, uint len)
{
Freehdr *fpp; /* Address of ptr to subtree */
Freehdr x;
Freehdr *left_hook; /* Temp for insertion */
Freehdr *right_hook; /* Temp for insertion */
Freehdr newhdr;
/*
* check for bad block size.
*/
if ( badblksize(newblk,len) ) {
error("insert: bad block size (%d) at %#x\n", len, newblk);
return;
}
/*
* Search for the first node which has a weight less
* than that of the new node; this will be the
* point at which we insert the new node.
*/
fpp = &_root;
x = *fpp;
while (weight(x) >= len) {
if (newblk < x->block)
fpp = &x->left;
else
fpp = &x->right;
x = *fpp;
}
/*
* Perform root insertion. The variable x traces a path through
* the fpp, and with the help of left_hook and right_hook,
* rewrites all links that cross the territory occupied
* by newblk.
*/
if ((newhdr = getfreehdr()) == NIL) {
/* Error message returned by getfreehdr() */
return;
}
*fpp = newhdr;
newhdr->left = NIL;
newhdr->right = NIL;
newhdr->block = newblk;
newhdr->size = len;
/*
* set length word in the block for consistency with the header.
*/
newblk->size = len;
left_hook = &newhdr->left;
right_hook = &newhdr->right;
while (x != NIL) {
/*
* Remark:
* The name 'left_hook' is somewhat confusing, since
* it is always set to the address of a .right link
* field. However, its value is always an address
* below (i.e., to the left of) newblk. Similarly
* for right_hook. The values of left_hook and
* right_hook converge toward the value of newblk,
* as in a classical binary search.
*/
if (x->block < newblk) {
/*
* rewrite link crossing from the left
*/
*left_hook = x;
left_hook = &x->right;
x = x->right;
} else {
/*
* rewrite link crossing from the right
*/
*right_hook = x;
right_hook = &x->left;
x = x->left;
} /*else*/
} /*while*/
*left_hook = *right_hook = NIL; /* clear remaining hooks */
} /*insert*/
/*
* delete(p)
* deletes a node from a cartesian tree. p is the address of
* a pointer to the node which is to be deleted.
*
* algorithm:
* The left and right branches of the node to be deleted define two
* subtrees which are to be merged and attached in place of the
* deleted node. Each node on the inside edges of these two
* subtrees is examined and longer nodes are placed above the
* shorter ones.
*
* On entry:
* *p is assumed to be non-null.
*/
static void
delete(Freehdr *p)
{
Freehdr x;
Freehdr left_branch; /* left subtree of deleted node */
Freehdr right_branch; /* right subtree of deleted node */
uint left_weight;
uint right_weight;
x = *p;
left_branch = x->left;
left_weight = weight(left_branch);
right_branch = x->right;
right_weight = weight(right_branch);
while (left_branch != right_branch) {
/*
* iterate until left branch and right branch are
* both NIL.
*/
if ( left_weight >= right_weight ) {
/*
* promote the left branch
*/
if (left_branch != NIL) {
if (left_weight == 0) {
/* zero-length block */
error("blocksize=0 at %#x\n",
(int)left_branch->block->data);
break;
}
*p = left_branch;
p = &left_branch->right;
left_branch = *p;
left_weight = weight(left_branch);
}
} else {
/*
* promote the right branch
*/
if (right_branch != NIL) {
if (right_weight == 0) {
/* zero-length block */
error("blocksize=0 at %#x\n",
(int)right_branch->block->data);
break;
}
*p = right_branch;
p = &right_branch->left;
right_branch = *p;
right_weight = weight(right_branch);
}
}/*else*/
}/*while*/
*p = NIL;
putfreehdr(x);
} /*delete*/
/*
* demote(p)
* Demotes a node in a cartesian tree, if necessary, to establish
* the required vertical ordering.
*
* algorithm:
* The left and right subtrees of the node to be demoted are to
* be partially merged and attached in place of the demoted node.
* The nodes on the inside edges of these two subtrees are
* examined and the longer nodes are placed above the shorter
* ones, until a node is reached which has a length no greater
* than that of the node being demoted (or until a null pointer
* is reached). The node is then attached at this point, and
* the remaining subtrees (if any) become its descendants.
*
* on entry:
* a. All the nodes in the tree, including the one to be demoted,
* must be correctly ordered horizontally;
* b. All the nodes except the one to be demoted must also be
* correctly positioned vertically. The node to be demoted
* may be already correctly positioned vertically, or it may
* have a length which is less than that of one or both of
* its progeny.
* c. *p is non-null
*/
static void
demote(Freehdr *p)
{
Freehdr x; /* addr of node to be demoted */
Freehdr left_branch;
Freehdr right_branch;
uint left_weight;
uint right_weight;
uint x_weight;
x = *p;
x_weight = weight(x);
left_branch = x->left;
right_branch = x->right;
left_weight = weight(left_branch);
right_weight = weight(right_branch);
while (left_weight > x_weight || right_weight > x_weight) {
/*
* select a descendant branch for promotion
*/
if (left_weight >= right_weight) {
/*
* promote the left branch
*/
*p = left_branch;
p = &left_branch->right;
left_branch = *p;
left_weight = weight(left_branch);
} else {
/*
* promote the right branch
*/
*p = right_branch;
p = &right_branch->left;
right_branch = *p;
right_weight = weight(right_branch);
} /*else*/
} /*while*/
*p = x; /* attach demoted node here */
x->left = left_branch;
x->right = right_branch;
} /*demote*/
/*
* char*
* malloc(nbytes)
* Allocates a block of length specified in bytes. If nbytes is
* zero, a valid pointer (that should not be dereferenced) is returned.
*
* algorithm:
* The freelist is searched by descending the tree from the root
* so that at each decision point the "better fitting" branch node
* is chosen (i.e., the shorter one, if it is long enough, or
* the longer one, otherwise). The descent stops when both
* branch nodes are too short.
*
* function result:
* Malloc returns a pointer to the allocated block. A null
* pointer indicates an error.
*
* diagnostics:
*
* ENOMEM: storage could not be allocated.
*
* EINVAL: either the argument was invalid, or the heap was found
* to be in an inconsistent state. More detailed information may
* be obtained by enabling range checks (cf., malloc_debug()).
*
* Note: In this implementation, each allocated block includes a
* length word, which occurs before the address seen by the caller.
* Allocation requests are rounded up to a multiple of wordsize.
*/
ptr_t
malloc(uint nbytes)
{
Freehdr allocp; /* ptr to node to be allocated */
Freehdr *fpp; /* for tree modifications */
Freehdr left_branch;
Freehdr right_branch;
uint left_weight;
uint right_weight;
Dblk retblk; /* block returned to the user */
/*
* if rigorous checking was requested, do it.
*/
if (debug_level >= 2) {
malloc_verify();
}
/*
* add the size of a length word to the request, and
* guarantee at least one word of usable data.
*/
nbytes += ALIGNSIZ;
if (nbytes < SMALLEST_BLK) {
nbytes = SMALLEST_BLK;
} else {
nbytes = roundup(nbytes, ALIGNSIZ);
}
/*
* ensure that at least one block is big enough to satisfy
* the request.
*/
if (weight(_root) < nbytes) {
/*
* the largest block is not enough.
*/
if(!morecore(nbytes))
return 0;
}
/*
* search down through the tree until a suitable block is
* found. At each decision point, select the better
* fitting node.
*/
fpp = &_root;
allocp = *fpp;
left_branch = allocp->left;
right_branch = allocp->right;
left_weight = weight(left_branch);
right_weight = weight(right_branch);
while (left_weight >= nbytes || right_weight >= nbytes) {
if (left_weight <= right_weight) {
if (left_weight >= nbytes) {
fpp = &allocp->left;
allocp = left_branch;
} else {
fpp = &allocp->right;
allocp = right_branch;
}
} else {
if (right_weight >= nbytes) {
fpp = &allocp->right;
allocp = right_branch;
} else {
fpp = &allocp->left;
allocp = left_branch;
}
}
left_branch = allocp->left;
right_branch = allocp->right;
left_weight = weight(left_branch);
right_weight = weight(right_branch);
} /*while*/
/*
* allocate storage from the selected node.
*/
if (allocp->size - nbytes <= SMALLEST_BLK) {
/*
* not big enough to split; must leave at least
* a dblk's worth of space.
*/
retblk = allocp->block;
delete(fpp);
} else {
/*
* Split the selected block n bytes from the top. The
* n bytes at the top are returned to the caller; the
* remainder of the block goes back to free space.
*/
Dblk nblk;
retblk = allocp->block;
nblk = nextblk(retblk, nbytes); /* ^next block */
nblk->size = allocp->size = retblk->size - nbytes;
__mallinfo.ordblks++; /* count fragments */
/*
* Change the selected node to point at the newly split
* block, and move the node to its proper place in
* the free space list.
*/
allocp->block = nblk;
demote(fpp);
/*
* set the length field of the allocated block; we need
* this because free() does not specify a length.
*/
retblk->size = nbytes;
}
/* maintain statistics */
__mallinfo.uordbytes += retblk->size; /* bytes allocated */
__mallinfo.allocated++; /* frags allocated */
if (nbytes < __mallinfo.mxfast)
__mallinfo.smblks++; /* kludge to pass the SVVS */
return((ptr_t)retblk->data);
} /*malloc*/
/*
* free(p)
* return a block to the free space tree.
*
* algorithm:
* Starting at the root, search for and coalesce free blocks
* adjacent to one given. When the appropriate place in the
* tree is found, insert the given block.
*
* Some sanity checks to avoid total confusion in the tree.
* If the block has already been freed, return.
* If the ptr is not from the sbrk'ed space, return.
* If the block size is invalid, return.
*/
free_t
free(ptr_t ptr)
{
uint nbytes; /* Size of node to be released */
Freehdr *fpp; /* For deletion from free list */
Freehdr neighbor; /* Node to be coalesced */
Dblk neighbor_blk; /* Ptr to potential neighbor */
uint neighbor_size; /* Size of potential neighbor */
Dblk oldblk; /* Ptr to block to be freed */
/*
* if rigorous checking was requested, do it.
*/
if (debug_level >= 2) {
malloc_verify();
}
/*
* Check the address of the old block.
*/
if ( misaligned(ptr) ) {
error("free: illegal address (%#x)\n", ptr);
free_return(0);
}
/*
* Freeing something that wasn't allocated isn't
* exactly kosher, but fclose() does it routinely.
*/
if( ptr < (ptr_t)_lbound || ptr > (ptr_t)_ubound ) {
errno = EINVAL;
free_return(0);
}
/*
* Get node length by backing up by the size of a header.
* Check for a valid length. It must be a positive
* multiple of ALIGNSIZ, at least as large as SMALLEST_BLK,
* no larger than the extent of the heap, and must not
* extend beyond the end of the heap.
*/
oldblk = (Dblk)((char*)ptr - ALIGNSIZ);
nbytes = oldblk->size;
if (badblksize(oldblk,nbytes)) {
error("free: bad block size (%d) at %#x\n",
(int)nbytes, (int)oldblk );
free_return(0);
}
/* maintain statistics */
__mallinfo.uordbytes -= nbytes; /* bytes allocated */
__mallinfo.allocated--; /* frags allocated */
/*
* Search the tree for the correct insertion point for this
* node, coalescing adjacent free blocks along the way.
*/
fpp = &_root;
neighbor = *fpp;
while (neighbor != NIL) {
neighbor_blk = neighbor->block;
neighbor_size = neighbor->size;
if (oldblk < neighbor_blk) {
Dblk nblk = nextblk(oldblk,nbytes);
if (nblk == neighbor_blk) {
/*
* Absorb and delete right neighbor
*/
nbytes += neighbor_size;
__mallinfo.ordblks--;
delete(fpp);
} else if (nblk > neighbor_blk) {
/*
* The block being freed overlaps
* another block in the tree. This
* is bad news. Return to avoid
* further fouling up the the tree.
*/
error("free: blocks %#x, %#x overlap\n",
(int)oldblk, (int)neighbor_blk);
free_return(0);
} else {
/*
* Search to the left
*/
fpp = &neighbor->left;
}
} else if (oldblk > neighbor_blk) {
Dblk nblk = nextblk(neighbor_blk, neighbor_size);
if (nblk == oldblk) {
/*
* Absorb and delete left neighbor
*/
oldblk = neighbor_blk;
nbytes += neighbor_size;
__mallinfo.ordblks--;
delete(fpp);
} else if (nblk > oldblk) {
/*
* This block has already been freed
*/
error("free: block %#x was already free\n",
(int)ptr);
free_return(0);
} else {
/*
* search to the right
*/
fpp = &neighbor->right;
}
} else {
/*
* This block has already been freed
* as "oldblk == neighbor_blk"
*/
error("free: block %#x was already free\n", (int)ptr);
free_return(0);
} /*else*/
/*
* Note that this depends on a side effect of
* delete(fpp) in order to terminate the loop!
*/
neighbor = *fpp;
} /*while*/
/*
* Insert the new node into the free space tree
*/
insert( oldblk, nbytes );
free_return(1);
} /*free*/
/*
* char*
* shrink(oldblk, oldsize, newsize)
* Decreases the size of an old block to a new size.
* Returns the remainder to free space. Returns the
* truncated block to the caller.
*/
static char *
shrink(Dblk oldblk, uint oldsize, uint newsize)
{
Dblk remainder;
if (oldsize - newsize >= SMALLEST_BLK) {
/*
* Block is to be contracted. Split the old block
* and return the remainder to free space.
*/
remainder = nextblk(oldblk, newsize);
remainder->size = oldsize - newsize;
oldblk->size = newsize;
/* maintain statistics */
__mallinfo.ordblks++; /* count fragments */
__mallinfo.allocated++; /* negate effect of free() */
free(remainder->data);
}
return(oldblk->data);
}
/*
* char*
* realloc(ptr, nbytes)
*
* Reallocate an old block with a new size, returning the old block
* if possible. The block returned is guaranteed to preserve the
* contents of the old block up to min(size(old block), newsize).
*
* For backwards compatibility, ptr is allowed to reference
* a block freed since the LAST call of malloc(). Thus the old
* block may be busy, free, or may even be nested within a free
* block.
*
* Some old programs have been known to do things like the following,
* which is guaranteed not to work:
*
* free(ptr);
* free(dummy);
* dummy = malloc(1);
* ptr = realloc(ptr,nbytes);
*
* This atrocity was found in the source for diff(1).
*/
ptr_t
realloc(ptr_t ptr, uint nbytes)
{
Freehdr *fpp;
Freehdr fp;
Dblk oldblk;
Dblk freeblk;
Dblk oldneighbor;
uint oldsize;
uint newsize;
uint oldneighborsize;
/*
* Add SVR4 semantics for OS 5.x so /usr/lib librarys
* work correctly when running in BCP mode
*/
if (ptr == NULL) {
return (malloc(nbytes));
}
/*
* if rigorous checking was requested, do it.
*/
if (debug_level >= 2) {
malloc_verify();
}
/*
* Check the address of the old block.
*/
if ( misaligned(ptr) ||
ptr < (ptr_t)_lbound ||
ptr > (ptr_t)_ubound ) {
error("realloc: illegal address (%#x)\n", ptr);
return(NULL);
}
/*
* check location and size of the old block and its
* neighboring block to the right. If the old block is
* at end of memory, the neighboring block is undefined.
*/
oldblk = (Dblk)((char*)ptr - ALIGNSIZ);
oldsize = oldblk->size;
if (badblksize(oldblk,oldsize)) {
error("realloc: bad block size (%d) at %#x\n",
oldsize, oldblk);
return(NULL);
}
oldneighbor = nextblk(oldblk,oldsize);
/* *** tree search code pulled into separate subroutine *** */
if (reclaim(oldblk, oldsize, 1) == -1) {
return(NULL); /* internal error */
}
/*
* At this point, we can guarantee that oldblk is out of free
* space. What we do next depends on a comparison of the size
* of the old block and the requested new block size. To do
* this, first round up the new size request.
*/
newsize = nbytes + ALIGNSIZ; /* add size of a length word */
if (newsize < SMALLEST_BLK) {
newsize = SMALLEST_BLK;
} else {
newsize = roundup(newsize, ALIGNSIZ);
}
/*
* Next, examine the size of the old block, and compare it
* with the requested new size.
*/
if (oldsize >= newsize) {
/*
* Block is to be made smaller.
*/
return(shrink(oldblk, oldsize, newsize));
}
/*
* Block is to be expanded. Look for adjacent free memory.
*/
if ( oldneighbor < (Dblk)_ubound ) {
/*
* Search for the adjacent block in the free
* space tree. Note that the tree may have been
* modified in the earlier loop.
*/
fpp = &_root;
fp = *fpp;
oldneighborsize = oldneighbor->size;
if ( badblksize(oldneighbor, oldneighborsize) ) {
error("realloc: bad blocksize(%d) at %#x\n",
oldneighborsize, oldneighbor);
return(NULL);
}
while ( weight(fp) >= oldneighborsize ) {
freeblk = fp->block;
if (oldneighbor < freeblk) {
/*
* search to the left
*/
fpp = &(fp->left);
fp = *fpp;
}
else if (oldneighbor > freeblk) {
/*
* search to the right
*/
fpp = &(fp->right);
fp = *fpp;
}
else { /* oldneighbor == freeblk */
/*
* neighboring block is free; is it big enough?
*/
if (oldsize + oldneighborsize >= newsize) {
/*
* Big enough. Delete freeblk, join
* oldblk to neighbor, return newsize
* bytes to the caller, and return the
* remainder to free storage.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.ordblks--;
__mallinfo.uordbytes += oldneighborsize;
oldsize += oldneighborsize;
oldblk->size = oldsize;
return(shrink(oldblk, oldsize, newsize));
} else {
/*
* Not big enough. Stop looking for a
* free lunch.
*/
break;
} /*else*/
} /*else*/
}/*while*/
} /*if*/
/*
* At this point, we know there is no free space in which to
* expand. Malloc a new block, copy the old block to the new,
* and free the old block, IN THAT ORDER.
*/
ptr = malloc(nbytes);
if (ptr != NULL) {
bcopy(oldblk->data, ptr, oldsize-ALIGNSIZ);
free(oldblk->data);
}
return(ptr);
} /* realloc */
/*
* *** The following code was pulled out of realloc() ***
*
* int
* reclaim(oldblk, oldsize, flag)
* If a block containing 'oldsize' bytes from 'oldblk'
* is in the free list, remove it from the free list.
* 'oldblk' and 'oldsize' are assumed to include the free block header.
*
* Returns 1 if block was successfully removed.
* Returns 0 if block was not in free list.
* Returns -1 if block spans a free/allocated boundary (error() called
* if 'flag' == 1).
*/
static int
reclaim(Dblk oldblk, uint oldsize, int flag)
{
Dblk oldneighbor;
Freehdr *fpp;
Freehdr fp;
Dblk freeblk;
uint size;
/*
* Search the free space list for a node describing oldblk,
* or a node describing a block containing oldblk. Assuming
* the size of blocks decreases monotonically with depth in
* the tree, the loop may terminate as soon as a block smaller
* than oldblk is encountered.
*/
oldneighbor = nextblk(oldblk, oldsize);
fpp = &_root;
fp = *fpp;
while ( (size = weight(fp)) >= oldsize ) {
freeblk = fp->block;
if (badblksize(freeblk,size)) {
error("realloc: bad block size (%d) at %#x\n",
size, freeblk);
return(-1);
}
if ( oldblk == freeblk ) {
/*
* |<-- freeblk ...
* _________________________________
* |<-- oldblk ...
* ---------------------------------
* Found oldblk in the free space tree; delete it.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.uordbytes += oldsize;
__mallinfo.allocated++;
return(1);
}
else if (oldblk < freeblk) {
/*
* |<-- freeblk ...
* _________________________________
* |<--oldblk ...
* ---------------------------------
* Search to the left for oldblk
*/
fpp = &fp->left;
fp = *fpp;
}
else {
/*
* |<-- freeblk ...
* _________________________________
* | |<--oldblk--->|<--oldneighbor
* ---------------------------------
* oldblk is somewhere to the right of freeblk.
* Check to see if it lies within freeblk.
*/
Dblk freeneighbor;
freeneighbor = nextblk(freeblk, freeblk->size);
if (oldblk >= freeneighbor) {
/*
* |<-- freeblk--->|<--- freeneighbor ...
* _________________________________
* | |<--oldblk--->|
* ---------------------------------
* no such luck; search to the right.
*/
fpp = &fp->right;
fp = *fpp;
}
else {
/*
* freeblk < oldblk < freeneighbor;
* i.e., oldblk begins within freeblk.
*/
if (oldneighbor > freeneighbor) {
/*
* |<-- freeblk--->|<--- freeneighbor
* _________________________________
* | |<--oldblk--->|<--oldneighbor
* ---------------------------------
* oldblk straddles a block boundary!
*/
if (flag) {
error("realloc: block %#x straddles free block boundary\n", oldblk);
}
return(-1);
}
else if ( oldneighbor == freeneighbor ) {
/*
* |<-------- freeblk------------->|
* _________________________________
* | |<--oldblk--->|
* ---------------------------------
* Oldblk is on the right end of
* freeblk. Delete freeblk, split
* into two fragments, and return
* the one on the left to free space.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.ordblks++;
__mallinfo.uordbytes += oldsize;
__mallinfo.allocated += 2;
freeblk->size -= oldsize;
free(freeblk->data);
return(1);
}
else {
/*
* |<-------- freeblk------------->|
* _________________________________
* | |oldblk | oldneighbor |
* ---------------------------------
* Oldblk is in the middle of freeblk.
* Delete freeblk, split into three
* fragments, and return the ones on
* the ends to free space.
*/
delete(fpp);
/* maintain statistics */
__mallinfo.ordblks += 2;
__mallinfo.uordbytes += freeblk->size;
__mallinfo.allocated += 3;
/*
* split the left fragment by
* subtracting the size of oldblk
* and oldblk's neighbor
*/
freeblk->size -=
( (char*)freeneighbor
- (char*)oldblk );
/*
* split the right fragment by
* setting oldblk's neighbor's size
*/
oldneighbor->size =
(char*)freeneighbor
- (char*)oldneighbor;
/*
* return the fragments to free space
*/
free(freeblk->data);
free(oldneighbor->data);
return(1);
} /*else*/
} /*else*/
} /* else */
} /*while*/
return(0); /* free block not found */
}
/*
* bool
* morecore(nbytes)
* Add a block of at least nbytes from end-of-memory to the
* free space tree.
*
* return value:
* true if at least n bytes can be allocated
* false otherwise
*
* remarks:
*
* -- free space (delimited by the extern variable _ubound) is
* extended by an amount determined by rounding nbytes up to
* a multiple of the system page size.
*
* -- The lower bound of the heap is determined the first time
* this routine is entered. It does NOT necessarily begin at
* the end of static data space, since startup code (e.g., for
* profiling) may have invoked sbrk() before we got here.
*/
static bool
morecore(uint nbytes)
{
Dblk p;
Freehdr newhdr;
if (nbpg == 0) {
nbpg = getpagesize();
/* hack to avoid fragmenting the heap with the first
freehdr page */
if ((newhdr = getfreehdr()) == NIL) {
/* Error message returned by getfreehdr() */
return(false);
}
(void)putfreehdr(newhdr);
}
nbytes = roundup(nbytes, nbpg);
p = (Dblk) sbrk((int)nbytes);
if (p == (Dblk) -1) {
if (errno == EAGAIN) errno = ENOMEM;
return(false); /* errno = ENOMEM */
}
if (_lbound == NULL) /* set _lbound the first time through */
_lbound = (char*) p;
_ubound = (char *) p + nbytes;
p->size = nbytes;
/* maintain statistics */
__mallinfo.arena = _ubound - _lbound;
__mallinfo.uordbytes += nbytes;
__mallinfo.ordblks++;
__mallinfo.allocated++;
free(p->data);
return(true);
} /*morecore*/
/*
* Get a free block header from the free header list.
* When the list is empty, allocate an array of headers.
* When the array is empty, allocate another one.
* When we can't allocate another array, we're in deep weeds.
*/
static Freehdr
getfreehdr(void)
{
Freehdr r;
Dblk blk;
uint size;
if (freehdrlist != NIL) {
r = freehdrlist;
freehdrlist = freehdrlist->left;
return(r);
}
if (nfreehdrs <= 0) {
size = NFREE_HDRS*sizeof(struct freehdr) + ALIGNSIZ;
blk = (Dblk) sbrk(size);
if ((int)blk == -1) {
malloc_debug(1);
error("getfreehdr: out of memory");
if (errno == EAGAIN) errno = ENOMEM;
return(NIL);
}
if (_lbound == NULL) /* set _lbound on first allocation */
_lbound = (char*)blk;
blk->size = size;
freehdrptr = (Freehdr)blk->data;
nfreehdrs = NFREE_HDRS;
_ubound = (char*) nextblk(blk,size);
/* maintain statistics */
__mallinfo.arena = _ubound - _lbound;
__mallinfo.treeoverhead += size;
}
nfreehdrs--;
return(freehdrptr++);
}
/*
* Free a free block header
* Add it to the list of available headers.
*/
static void
putfreehdr(Freehdr p)
{
p->left = freehdrlist;
freehdrlist = p;
}
#ifndef DEBUG /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
/*
* stubs for error handling and diagnosis routines. These are what
* you get in the standard C library; for non-placebo diagnostics
* load /usr/lib/malloc.debug.o with your program.
*/
/*ARGSUSED*/
static void
error(char *fmt, ...)
{
errno = EINVAL;
}
#endif /* !DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
#ifdef DEBUG /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
/*
* malloc_debug(level)
*
* description:
*
* Controls the level of error diagnosis and consistency checking
* done by malloc() and free(). level is interpreted as follows:
*
* 0: malloc() and free() return 0 if error detected in arguments
* (errno is set to EINVAL)
* 1: malloc() and free() abort if errors detected in arguments
* 2: same as 1, but scan entire heap for errors on every call
* to malloc() or free()
*
* function result:
* returns the previous level of error reporting.
*/
int
malloc_debug(int level)
{
int old_level;
old_level = debug_level;
debug_level = level;
return (old_level);
}
/*
* check a free space tree pointer. Should be in
* the static free pool or somewhere in the heap.
*/
#define chkblk(p)\
if ( misaligned(p)\
|| ((Dblk)(p) < (Dblk)_lbound || (Dblk)(p) > (Dblk)_ubound)){\
blkerror(p);\
return 0;\
}
#define chkhdr(p) chkblk(p)
static
blkerror(Freehdr p)
{
error("Illegal block address (%#x)\n", (p));
}
/*
* cartesian(p)
* returns 1 if free space tree p satisfies internal consistency
* checks.
*/
static int
cartesian(Freehdr p)
{
Freehdr probe;
Dblk db,pdb;
if (p == NIL) /* no tree to test */
return 1;
/*
* check that root has a data block
*/
chkhdr(p);
pdb = p->block;
chkblk(pdb);
/*
* check that the child blocks are no larger than the parent block.
*/
probe = p->left;
if (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if (probe->size > p->size) /* child larger than parent */
return 0;
}
probe = p->right;
if (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if (probe->size > p->size) /* child larger than parent */
return 0;
}
/*
* test data addresses in the left subtree,
* starting at the left subroot and probing to
* the right. All data addresses must be < p->block.
*/
probe = p->left;
while (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if ( nextblk(db, probe->size) >= pdb ) /* overlap */
return 0;
probe = probe->right;
}
/*
* test data addresses in the right subtree,
* starting at the right subroot and probing to
* the left. All addresses must be > nextblk(p->block).
*/
pdb = nextblk(pdb, p->size);
probe = p->right;
while (probe != NIL) {
chkhdr(probe);
db = probe->block;
chkblk(db);
if (db == NULL || db <= pdb) /* overlap */
return 0;
probe = probe->left;
}
return (cartesian(p->left) && cartesian(p->right));
}
/*
* malloc_verify()
*
* This is a verification routine. It walks through all blocks
* in the heap (both free and busy) and checks for bad blocks.
* malloc_verify returns 1 if the heap contains no detectably bad
* blocks; otherwise it returns 0.
*/
int
malloc_verify(void)
{
int maxsize;
int hdrsize;
int size;
Dblk p;
uint lb,ub;
extern char end[];
if (_lbound == NULL) /* no allocation yet */
return 1;
/*
* first check heap bounds pointers
*/
lb = (uint)end;
ub = (uint)sbrk(0);
if ((uint)_lbound < lb || (uint)_lbound > ub) {
error("malloc_verify: illegal heap lower bound (%#x)\n",
_lbound);
return 0;
}
if ((uint)_ubound < lb || (uint)_ubound > ub) {
error("malloc_verify: illegal heap upper bound (%#x)\n",
_ubound);
return 0;
}
maxsize = heapsize();
p = (Dblk)_lbound;
while (p < (Dblk) _ubound) {
size = p->size;
if ( (size) < SMALLEST_BLK
|| (size) & (ALIGNSIZ-1)
|| (size) > heapsize()
|| ((char*)(p))+(size) > _ubound ) {
error("malloc_verify: bad block size (%d) at %#x\n",
size, p);
return(0); /* Badness */
}
p = nextblk(p, size);
}
if (p > (Dblk) _ubound) {
error("malloc_verify: heap corrupted\n");
return(0);
}
if (!cartesian(_root)){
error("malloc_verify: free space tree corrupted\n");
return(0);
}
return(1);
}
/*
* The following is a kludge to avoid dependency on stdio, which
* uses malloc() and free(), one of which probably got us here in
* the first place.
*/
#define putchar(c) (*buf++ = (c))
extern int fileno(); /*bletch*/
#define stderr 2 /*bletch*/
#define LBUFSIZ 256
static char stderrbuf[LBUFSIZ];
/*
* Error routine.
* If debug_level == 0, does nothing except set errno = EINVAL.
* Otherwise, prints an error message to stderr and generates a
* core image.
*/
static void
error(char *fmt, ...)
{
static int n = 0; /* prevents infinite recursion when using stdio */
int nbytes;
va_list ap;
errno = EINVAL;
if (debug_level == 0)
return;
if (!n++) {
va_start(ap, fmt);
nbytes = vsprintf(stderrbuf, fmt, ap);
va_end(ap);
stderrbuf[nbytes++] = '\n';
stderrbuf[nbytes] = '\0';
write(fileno(stderr), stderrbuf, nbytes);
}
abort();
}
#endif /* DEBUG <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
|