|
|
1.1 root 1: /*
2: * Implementation of functions prototyped in "buildobj.h"
3: */
4: /*
5: *-IMPORTS:
6: * <sys/compat.h>
7: * USE_PROTO
8: * VOID
9: * ARGS ()
10: * <stdlib.h>
11: * NULL
12: * size_t
13: * free ()
14: * malloc ()
15: * <string.h>
16: * memcpy ()
17: * memset ()
18: */
19:
20: #include <sys/compat.h>
21: #include <stdlib.h>
22: #include <string.h>
23:
24: #include "buildobj.h"
25:
26:
27: /*
28: * Structures that are only really visible inside this compilation unit.
29: *
30: * Note that "o_prev" is used only for nested build contexts, which are
31: * special, although it could be used for all objects if we wanted to spend
32: * the space on allowing stacklike allocation (which we don't, normally).
33: */
34:
35:
36: typedef struct buildchunk bchunk_t;
37: typedef struct objinfo obj_t;
38:
39: struct objinfo {
40: obj_t * o_prev; /* previous object */
41: VOID * o_base; /* base of object */
42: size_t o_size; /* size of object */
43: unsigned char o_building; /* object being built */
44: };
45:
46: struct builder {
47: bchunk_t * b_first; /* first allocated chunk */
48: bchunk_t * b_last; /* last allocated chunk */
49: size_t b_chunksize; /* recommended chunk size */
50: size_t b_align; /* allocation alignment factor */
51:
52: obj_t b_obj; /* current object */
53: bchunk_t * b_chunkp; /* chunk of last/current object */
54: };
55:
56: struct buildchunk {
57: bchunk_t * bc_next; /* next memory chunk */
58: char * bc_base; /* base of chunk space */
59: size_t bc_free; /* amount of free space */
60: };
61:
62:
63: /*
64: * This internal function allocates a new chunk of memory to be parcelled out
65: * to clients. Allocate "b_chunksize" bytes of memory to give out, and copy
66: * any partial allocation over to the new chunk (since we typically only
67: * allocate new chunks when a request to expand a previous allocation
68: * overflows the biggest available block of space).
69: */
70:
71: #if USE_PROTO
72: bchunk_t * (BUILD_CHUNK) (build_t * heap, bchunk_t * bchunkp)
73: #else
74: bchunk_t *
75: BUILD_CHUNK ARGS ((heap, bchunkp))
76: build_t * heap;
77: bchunk_t * bchunkp;
78: #endif
79: {
80:
81: /*
82: * Ok, let's add a new chunk.
83: */
84:
85: if ((bchunkp = (bchunk_t *) malloc (heap->b_chunksize +
86: sizeof (bchunk_t))) == NULL)
87: return NULL;
88:
89: bchunkp->bc_base = (char *) (bchunkp + 1);
90: bchunkp->bc_free = heap->b_chunksize;
91:
92: /*
93: * Copy any previously existing data to the new chunk.
94: */
95:
96: if (heap->b_obj.o_size > 0) {
97:
98: memcpy (bchunkp->bc_base, heap->b_obj.o_base,
99: heap->b_obj.o_size);
100:
101: bchunkp->bc_base += heap->b_obj.o_size;
102: bchunkp->bc_free -= heap->b_obj.o_size;
103: }
104:
105:
106: heap->b_chunkp = bchunkp;
107: heap->b_obj.o_base = (char *) (bchunkp + 1);
108:
109: return bchunkp;
110: }
111:
112:
113: /*
114: * This internal function increases the size of an allocation. If a request
115: * causes an allocation to expand beyond the end of the current chunk, try and
116: * find another chunk that can hold it, and if that fails allocate a new chunk
117: * which is guaranteed to be large enough.
118: *
119: * The client's data is copied to the new space, appended to any previously
120: * allocated data in the same group.
121: */
122:
123: #if USE_PROTO
124: int (BUILD_ADD) (build_t * heap, size_t size, CONST VOID * init)
125: #else
126: int
127: BUILD_ADD ARGS ((heap, size, init))
128: build_t * heap;
129: size_t size;
130: CONST VOID * init;
131: #endif
132: {
133: bchunk_t * bchunkp;
134: size_t total = size + heap->b_obj.o_size;
135:
136: if (size == 0)
137: return BUILD_OK;
138:
139: if (total < size || total + sizeof (bchunk_t) < total)
140: return BUILD_SIZE_OVERFLOW;
141:
142: /*
143: * First, try to fit the expanded allocation in the current chunk.
144: * If there are no chunks allocated yet, let's start.
145: */
146:
147: bchunkp = heap->b_chunkp;
148:
149: if ((bchunkp != NULL && bchunkp->bc_free < size) || bchunkp == NULL) {
150: /*
151: * First, try and find an already allocated chunk that will
152: * hold the expanded allocation.
153: */
154:
155: while (bchunkp != NULL) {
156:
157: if (bchunkp->bc_free >= total) {
158: /*
159: * Transfer existing object information to new
160: * chunk.
161: */
162:
163: if (heap->b_obj.o_size > 0)
164: memcpy (bchunkp->bc_base,
165: heap->b_obj.o_base,
166: heap->b_obj.o_size);
167:
168: heap->b_obj.o_base = bchunkp->bc_base;
169: heap->b_chunkp = bchunkp;
170:
171: bchunkp->bc_base += heap->b_obj.o_size;
172: bchunkp->bc_free -= heap->b_obj.o_size;
173:
174: goto gotmem;
175: }
176:
177: bchunkp = bchunkp->bc_next;
178: }
179:
180: if (total > heap->b_chunksize)
181: heap->b_chunksize = total + (total >> 1);
182:
183: if ((bchunkp = BUILD_CHUNK (heap, bchunkp)) == NULL)
184: return BUILD_NO_MEMORY;
185: }
186:
187: gotmem:
188: /*
189: * OK, let's initialise the new space.
190: */
191:
192: if (init == INIT_ZERO) {
193: /*
194: * The client passed us the special pointer value that means
195: * we should zero the new space.
196: */
197:
198: memset (bchunkp->bc_base, 0, size);
199: } else {
200: /*
201: * The client has given us explicit initialisation values.
202: */
203:
204: memcpy (bchunkp->bc_base, init, size);
205: }
206:
207: bchunkp->bc_base += size;
208: bchunkp->bc_free -= size;
209: heap->b_obj.o_size += size;
210:
211: return BUILD_OK;
212: }
213:
214:
215: /*
216: * Local function which recovers a chunk number from an object address. Note
217: * that we *cannot* do this by merely scanning the chunks looking for one that
218: * encloses the object pointer we have, because relational comparisons between
219: * distinctly-allocated objects yields an undefined result (ie, we could get
220: * false positives).
221: *
222: * However, we can solve the problem by requiring the object pointer to be the
223: * most recent allocation in a given chunk, and passing in the size of the
224: * object. This way, we can test for a match via equality, which is a stronger
225: * test, because two object pointers compare equal if and only if they point
226: * at the same object.
227: *
228: * [ Note that the converse is controversial; it does not appear to be
229: * mandated by ISO C, and the notion of object identity is hotly debated
230: * in C++ circles. Since we will be using pointers that have been derived
231: * via convential means we can ignore such notions of aliasing. ]
232: */
233:
234: #if USE_PROTO
235: LOCAL bchunk_t * (FIND_CHUNK) (build_t * heap, VOID * obj, size_t size)
236: #else
237: LOCAL bchunk_t *
238: FIND_CHUNK ARGS ((heap, obj, size))
239: build_t * heap;
240: VOID * obj;
241: size_t size;
242: #endif
243: {
244: bchunk_t * scan;
245:
246: for (scan = heap->b_first ; scan != NULL ; scan = scan->bc_next)
247: if (scan->bc_base - size == obj)
248: return scan;
249:
250: return NULL;
251: }
252:
253:
254: /*
255: * This function allocates a control block for building variable-sized
256: * objects with. The "chunksize" is the default amount of memory to request
257: * from the C library in a block which other functions will parcel out to
258: * clients.
259: */
260:
261: #if USE_PROTO
262: build_t * (builder_alloc) (size_t chunksize, size_t align)
263: #else
264: build_t *
265: builder_alloc ARGS ((chunksize, align))
266: size_t chunksize;
267: size_t align;
268: #endif
269: {
270: build_t * buildp;
271:
272: if (chunksize < 256)
273: chunksize = 256;
274:
275: if ((buildp = (build_t *) malloc (sizeof (build_t))) != NULL) {
276:
277: buildp->b_first = buildp->b_last = NULL;
278: buildp->b_chunksize = chunksize;
279: buildp->b_align = align == 0 ? 1 : align;
280:
281: buildp->b_obj.o_building = 0;
282: buildp->b_obj.o_prev = NULL; /* no previous object */
283: buildp->b_obj.o_base = NULL; /* not building an object */
284: }
285:
286: return buildp;
287: }
288:
289:
290: /*
291: * Free the basic object builder structure and all the chunk memory that it
292: * attached to itself during its lifetime.
293: */
294:
295: #if USE_PROTO
296: void (builder_free) (build_t * heap)
297: #else
298: void
299: builder_free ARGS ((heap))
300: build_t * heap;
301: #endif
302: {
303: bchunk_t * scan;
304: bchunk_t * next;
305:
306: if (heap == NULL)
307: return;
308:
309: for (scan = heap->b_first ; scan != NULL ; scan = next) {
310:
311: next = scan->bc_next;
312: free (scan);
313: }
314:
315: free (heap);
316: }
317:
318:
319: /*
320: * Simple function to allocate just a single chunk.
321: */
322:
323: #if USE_PROTO
324: VOID * (build_malloc) (build_t * heap, size_t size)
325: #else
326: VOID *
327: build_malloc ARGS ((heap, size))
328: build_t * heap;
329: size_t size;
330: #endif
331: {
332: if (heap == NULL || heap->b_obj.o_building != 0 ||
333: (heap->b_obj.o_base != NULL && heap->b_obj.o_prev != NULL))
334: return NULL;
335:
336: heap->b_obj.o_size = 0;
337: heap->b_obj.o_base = NULL;
338:
339: heap->b_chunkp = heap->b_first;
340:
341: #ifdef __COHERENT__
342: return BUILD_ADD (heap, size, NULL) == 0 ? heap->b_obj.o_base :
343: (VOID *) NULL;
344: #else
345: return BUILD_ADD (heap, size, NULL) == 0 ? heap->b_obj.o_base : NULL;
346: #endif
347: }
348:
349:
350: /*
351: * Begin building some variable-sized object with an initial allocation of
352: * "size" bytes. All previous allocations must be complete before beginning a
353: * new allocation.
354: */
355:
356: #if USE_PROTO
357: int (build_begin) (build_t * heap, size_t size, CONST VOID * init)
358: #else
359: int
360: build_begin ARGS ((heap, size, init))
361: build_t * heap;
362: size_t size;
363: CONST VOID * init;
364: #endif
365: {
366: if (heap == NULL)
367: return BUILD_NULL_HEAP;
368: if (heap->b_obj.o_building != 0)
369: return BUILD_OBJECT_BEGUN;
370:
371: /*
372: * We only allow one object to be built per inner nesting level; since
373: * object descriptors are normally not needed, levels that can be
374: * unwound have to be pushed/popped manually. However, only one object
375: * can be built at an inner level or else the stack could not be
376: * unwound.
377: */
378:
379: if (heap->b_obj.o_base != NULL &&
380: heap->b_obj.o_prev != NULL)
381: return BUILD_BAD_NESTING;
382:
383: heap->b_obj.o_size = 0;
384: heap->b_obj.o_base = NULL;
385: heap->b_obj.o_building = 1;
386:
387: heap->b_chunkp = heap->b_first;
388:
389: return BUILD_ADD (heap, size, init);
390: }
391:
392:
393: /*
394: * Add "size" bytes to the current variable-sized object.
395: */
396:
397: #if USE_PROTO
398: int (build_add) (build_t * heap, size_t size, CONST VOID * init)
399: #else
400: int
401: build_add ARGS ((heap, size, init))
402: build_t * heap;
403: size_t size;
404: CONST VOID * init;
405: #endif
406: {
407: if (heap == NULL)
408: return BUILD_NULL_HEAP;
409: if (heap->b_obj.o_building == 0)
410: return BUILD_NO_OBJECT;
411:
412: return BUILD_ADD (heap, size, init);
413: }
414:
415:
416: /*
417: * Special-case function to add a single character to an object efficiently.
418: * This is useful for functions which must build strings on the fly without
419: * any easy way of predetermining the size.
420: */
421:
422: #if USE_PROTO
423: int (build_addchar) (build_t * heap, char ch)
424: #else
425: int
426: build_addchar ARGS ((heap, ch))
427: build_t * heap;
428: char ch;
429: #endif
430: {
431: bchunk_t * bchunkp;
432:
433: if (heap == NULL)
434: return BUILD_NULL_HEAP;
435: if (heap->b_obj.o_building == 0)
436: return BUILD_NO_OBJECT;
437:
438: /*
439: * Like BUILD_ADD (), but faster in the simplest case.
440: */
441:
442: if ((bchunkp = heap->b_chunkp) == NULL || bchunkp->bc_free == 0)
443: if ((bchunkp = BUILD_CHUNK (heap, bchunkp)) == NULL)
444: return BUILD_NO_MEMORY;
445:
446: * bchunkp->bc_base ++ = ch;
447: bchunkp->bc_free --;
448: heap->b_obj.o_size ++;
449:
450: return BUILD_OK;
451: }
452:
453:
454: /*
455: * End construction of an object, optionally returning the size in bytes
456: * occupied.
457: */
458:
459: #if USE_PROTO
460: VOID * (build_end) (build_t * heap, size_t * size)
461: #else
462: VOID *
463: build_end ARGS ((heap, size))
464: build_t * heap;
465: size_t * size;
466: #endif
467: {
468: size_t adjust;
469:
470: if (heap == NULL || heap->b_obj.o_building == 0)
471: return NULL;
472:
473: if (size != NULL)
474: * size = heap->b_obj.o_size;
475:
476: if ((adjust = heap->b_obj.o_size & (heap->b_align - 1)) != 0) {
477: /*
478: * Waste some space at the end to make the alignment happen.
479: */
480:
481: adjust = heap->b_align - adjust;
482:
483: heap->b_obj.o_size += adjust;
484: heap->b_chunkp->bc_base += adjust;
485: heap->b_chunkp->bc_free -= adjust;
486: }
487:
488: heap->b_obj.o_building = 0;
489: return heap->b_obj.o_base;
490: }
491:
492:
493:
494: /*
495: * Return the storage of the most recently constructed object to the free
496: * pool. Note that the GNU obstack system allows an object heap to be cut back
497: * to any arbitrary point in a stack-like fashion; we don't, because we try
498: * and squeeze space into end-of-chunk areas that the GNU system would leave
499: * alone. Perhaps later we can add options to allow this.
500: */
501:
502: #if USE_PROTO
503: int (build_release) (build_t * heap, CONST VOID * base)
504: #else
505: int
506: build_release ARGS ((heap, base))
507: build_t * heap;
508: CONST VOID * base;
509: #endif
510: {
511: if (heap == NULL)
512: return BUILD_NULL_HEAP;
513: if (base == NULL)
514: return BUILD_NULL_BASE;
515: if (heap->b_obj.o_building == 1)
516: return BUILD_OBJECT_BEGUN;
517: if (heap->b_obj.o_base != base)
518: return BUILD_NOT_LAST;
519:
520: /*
521: * Return "heap->b_size" bytes to the chunk that the last object was
522: * constructed in. We also forget that the object was ever built,
523: * which will allow nested builds to happen.
524: */
525:
526: heap->b_chunkp->bc_base -= heap->b_obj.o_size;
527: heap->b_chunkp->bc_free += heap->b_obj.o_size;
528:
529: heap->b_obj.o_base = NULL;
530: heap->b_obj.o_size = 0;
531:
532: return BUILD_OK;
533: }
534:
535:
536: /*
537: * Temporarily suspend construction of an object and begin nested construction
538: * of another object. Once a nested build context has been entered, only one
539: * object may be allowed to be built (although it may be thrown away and
540: * another one begun).
541: */
542:
543: #if USE_PROTO
544: int (build_push) (build_t * heap)
545: #else
546: int
547: build_push ARGS ((heap))
548: build_t * heap;
549: #endif
550: {
551: obj_t temp;
552: int err;
553:
554: if (heap == NULL)
555: return BUILD_NULL_HEAP;
556:
557: /*
558: * We will need to find some space in the heap for the saved build
559: * context. We take a snapshot of the allocation context in a local
560: * variable for eventual storage in the heap, and start a "new"
561: * allocation. Then, calling BUILD_ADD () will find space for the
562: * snapshot and copy it.
563: */
564:
565: temp = heap->b_obj;
566:
567: heap->b_obj.o_size = 0;
568:
569: if ((err = BUILD_ADD (heap, sizeof (temp), & temp)) != 0) {
570: /*
571: * We couldn't create space for the saved context, so restore
572: * the state and return the error to the caller.
573: */
574:
575: heap->b_obj = temp;
576: } else {
577: /*
578: * Record the location where the saved snapshot was stored as
579: * the back-link in the snapshot chain. Then, we clear the
580: * top object record for use in at most one nested allocation.
581: */
582:
583: heap->b_obj.o_prev = (obj_t *) heap->b_obj.o_base;
584:
585: heap->b_obj.o_size = 0;
586: heap->b_obj.o_base = NULL;
587: }
588:
589: return err;
590: }
591:
592:
593: /*
594: * Undo the actions of build_push (). If there is an allocation in the current
595: * top-level object record, discard it implicitly.
596: */
597:
598: #if USE_PROTO
599: int (build_pop) (build_t * heap)
600: #else
601: int
602: build_pop ARGS ((heap))
603: build_t * heap;
604: #endif
605: {
606: bchunk_t * temp;
607:
608: if (heap == NULL)
609: return BUILD_NULL_HEAP;
610: if (heap->b_obj.o_prev == NULL)
611: return BUILD_STACK_EMPTY;
612:
613: /*
614: * Before we retrieve the previous object context, we have to find
615: * the chunk it is stored in (so that we can discard the saved
616: * context record).
617: */
618:
619: if ((temp = FIND_CHUNK (heap, heap->b_obj.o_prev,
620: sizeof (obj_t))) == NULL)
621: return BUILD_CORRUPT;
622:
623: /*
624: * Implicitly discard top object.
625: */
626:
627: if (heap->b_obj.o_base != NULL) {
628:
629: heap->b_chunkp->bc_base -= heap->b_obj.o_size;
630: heap->b_chunkp->bc_free += heap->b_obj.o_size;
631: }
632:
633:
634: /*
635: * Free saved record and recover the data stored there. We do it in
636: * that backwards order to avoid storing too many temporary pointers,
637: * and because it is safe for us (we won't be interrupted, and our
638: * code doesn't overwrite newly-freed memory).
639: */
640:
641: temp->bc_base -= sizeof (obj_t);
642: temp->bc_free += sizeof (obj_t);
643:
644: heap->b_obj = * heap->b_obj.o_prev;
645:
646:
647: /*
648: * We don't store the previous chunk pointer in the object record
649: * because we can recover it easily if necessary (and it often isn't).
650: */
651:
652: if (heap->b_obj.o_base != NULL &&
653: (heap->b_chunkp = FIND_CHUNK (heap, heap->b_obj.o_base,
654: heap->b_obj.o_size)) == NULL)
655: return BUILD_CORRUPT;
656:
657: return 0;
658: }
659:
660:
661: /*
662: * Return a human-readable string from an error code.
663: */
664:
665: #if USE_PROTO
666: CONST char * (build_error) (int errcode)
667: #else
668: CONST char *
669: build_error ARGS ((errcode))
670: int errcode;
671: #endif
672: {
673: static CONST char * errors [] = {
674: /*
675: * Error strings to match the error enumeration, from 0 to
676: * the maximum negative error code, plus an extra code for
677: * invalid error codes.
678: */
679:
680: "no error",
681: "\"heap\" parameter is NULL",
682: "\"base\" parameter is NULL",
683: "object already under construction",
684: "object construction not begun",
685: "insufficient memory to satisfy request",
686: "overflow of \"size_t\" would result",
687: "not most recently built object",
688: "can build only one object in inner nesting level",
689: "pop of empty saved object stack",
690: "heap appears to be corrupt",
691: "<not a valid error>"
692: };
693:
694: #define ARRAY_LEN(a) (sizeof (a) / sizeof (* (a)))
695:
696: return (errcode > 0 || - errcode >= ARRAY_LEN (errors) ?
697: errors [ARRAY_LEN (errors) - 1] : errors [- errcode]);
698: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.