2 * block - fixed, dynamic, fifo and circular memory blocks
4 * Copyright (C) 1999-2007 Landon Curt Noll and Ernest Bowen
6 * Primary author: Landon Curt Noll
8 * Calc is open software; you can redistribute it and/or modify it under
9 * the terms of the version 2.1 of the GNU Lesser General Public License
10 * as published by the Free Software Foundation.
12 * Calc is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15 * Public License for more details.
17 * A copy of version 2.1 of the GNU Lesser General Public License is
18 * distributed with calc under the filename COPYING-LGPL. You should have
19 * received a copy with calc; if not, write to Free Software Foundation, Inc.
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * @(#) $Revision: 30.1 $
23 * @(#) $Id: block.c,v 30.1 2007/03/16 11:09:46 chongo Exp $
24 * @(#) $Source: /usr/local/src/bin/calc/RCS/block.c,v $
26 * Under source code control: 1997/02/27 00:29:40
27 * File existed as early as: 1997
29 * chongo <was here> /\oo/\ http://www.isthe.com/chongo/
30 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
43 #define NBLOCKCHUNK 16
45 STATIC
long nblockcount
= 0;
46 STATIC
long maxnblockcount
= 0;
47 STATIC STRINGHEAD nblocknames
;
48 STATIC NBLOCK
**nblocks
;
51 /* forward declarations */
52 S_FUNC
void blkchk(BLOCK
*);
56 * blkalloc - allocate a block
59 * len - initial memory length of the block
61 * chunk - allocation chunk size
64 * pointer to a newly allocated BLOCK
67 blkalloc(int len
, int chunk
)
69 BLOCK
*nblk
; /* new block allocated */
77 chunk
= BLK_CHUNKSIZE
;
82 nblk
= (BLOCK
*)malloc(sizeof(BLOCK
));
84 math_error("cannot allocate block");
91 nblk
->blkchunk
= chunk
;
92 nblk
->maxsize
= ((len
+chunk
)/chunk
)*chunk
;
93 nblk
->data
= (USB8
*)malloc(nblk
->maxsize
);
94 if (nblk
->data
== NULL
) {
95 math_error("cannot allocate block data storage");
98 memset(nblk
->data
, 0, nblk
->maxsize
);
104 if (conf
->calc_debug
& CALCDBG_BLOCK
) {
112 * blk_free - free a block
114 * NOTE: THIS IS NOT THE CALC blktrunc() BUILTIN FUNCTION!! This
115 * is what is called to free block storage.
118 * blk - the block to free
123 /* free if non-NULL */
126 /* free data storage */
127 if (blk
->data
!= NULL
) {
139 * blkchk - check the sanity of a block
141 * These checks should never fail if calc is working correctly. During
142 * debug time, we plan to call this function often. Once we are satisfied,
143 * we will normally call this code only in a few places.
145 * If "calc_debug" has the bit corresponding to CALCDBG_BLOCK set, this
146 * function is called during execution of the following builtins:
148 * alloc(), realloc(), free()
151 * blk - the BLOCK to check
154 * if all is ok, otherwise math_error() is called and this
155 * function does not return
162 * firewall - general sanity check
164 if ((conf
->calc_debug
& CALCDBG_BLOCK
) == 0) {
165 /* do nothing when debugging is disabled */
169 math_error("internal: blk ptr is NULL");
174 * pointers must not be NULL
176 if (blk
->data
== NULL
) {
177 math_error("internal: blk->data ptr is NULL");
184 if (blk
->datalen
< 0) {
185 math_error("internal: blk->datalen < 0");
190 * check the datalen and datalen2 values
192 if (blk
->datalen
< 0) {
193 math_error("internal: blk->datalen < 0");
201 * blkrealloc - reallocate a block
203 * Reallocation of a block can change several aspects of a block.
205 * It can change the much data it holds or can hold.
207 * It can change the memory footprint (in terms of
208 * how much storage is malloced for current or future use).
210 * It can change the chunk size used to grow malloced size
211 * as the data size grows.
213 * Each of the len and chunksize may be kept the same.
216 * blk - old BLOCK to reallocate
217 * newlen - how much data the block holds
218 * newchunk - allocation chunk size (<0 ==> no change, 0 == default)
221 blkrealloc(BLOCK
*blk
, int newlen
, int newchunk
)
223 USB8
*nblk
; /* realloced storage */
224 int newmax
; /* new maximum stoage size */
229 if (conf
->calc_debug
& CALCDBG_BLOCK
) {
236 /* newlen < 0 means do not change the length */
238 newlen
= blk
->datalen
;
240 /* newchunk <= 0 means do not change the chunk size */
242 newchunk
= blk
->blkchunk
;
243 } else if (newchunk
== 0) {
244 newchunk
= BLK_CHUNKSIZE
;
248 * reallocate storage if we have a different allocation size
250 newmax
= ((newlen
+newchunk
)/newchunk
)*newchunk
;
251 if (newmax
!= blk
->maxsize
) {
253 /* reallocate new storage */
254 nblk
= (USB8
*)realloc(blk
->data
, newmax
);
256 math_error("cannot reallocate block storage");
260 /* clear any new storage */
261 if (newmax
> blk
->maxsize
) {
262 memset(nblk
+blk
->maxsize
, 0, (newmax
-blk
->maxsize
));
264 blk
->maxsize
= newmax
;
266 /* restore the data pointers */
271 * deal the case of a newlen == 0 early and return
276 * setup the empty buffer
278 * We know that newtype is not circular since we force
279 * newlen to be at least 1 (because circular blocks
280 * always have at least one unused octet).
282 if (blk
->datalen
< blk
->maxsize
) {
283 memset(blk
->data
, 0, blk
->datalen
);
285 memset(blk
->data
, 0, blk
->maxsize
);
288 if (conf
->calc_debug
& CALCDBG_BLOCK
) {
295 * Set the data length
297 * We also know that the new block is not empty since we have
298 * already dealth with that case above.
300 * After this section of code, limit and datalen will be
301 * correct in terms of the new type.
303 if (newlen
> blk
->datalen
) {
305 /* there is new storage, clear it */
306 memset(blk
->data
+ blk
->datalen
, 0, newlen
-blk
->datalen
);
307 /* growing storage for blocks grows the data */
308 blk
->datalen
= newlen
;
310 } else if (newlen
<= blk
->datalen
) {
312 /* the block will be full */
313 blk
->datalen
= newlen
;
317 * return realloced type
319 if (conf
->calc_debug
& CALCDBG_BLOCK
) {
327 * blktrunc - truncate a BLOCK down to a minimal fixed block
329 * NOTE: THIS IS NOT THE INTERNAL CALC FREE FUNCTION!! This
330 * is what blktrunc() builtin calls to reduce storage of a block
331 * down to an absolute minimum.
333 * This actually forms a zero length fixed block with a chunk of 1.
336 * blk - the BLOCK to shrink
339 * pointer to a newly allocated BLOCK
347 if (conf
->calc_debug
& CALCDBG_BLOCK
) {
352 * free the old storage
357 * setup as a zero length fixed block
362 blk
->data
= (USB8
*)malloc(1);
363 if (blk
->data
== NULL
) {
364 math_error("cannot allocate truncated block storage");
367 blk
->data
[0] = (USB8
)0;
368 if (conf
->calc_debug
& CALCDBG_BLOCK
) {
376 * blk_copy - copy a block
379 * blk - the block to copy
382 * pointer to copy of blk
387 BLOCK
*nblk
; /* copy of blk */
392 nblk
= (BLOCK
*)malloc(sizeof(BLOCK
));
394 math_error("blk_copy: cannot malloc BLOCK");
399 * duplicate most of the block
404 * duplicate block data
406 nblk
->data
= (USB8
*)malloc(blk
->maxsize
);
407 if (nblk
->data
== NULL
) {
408 math_error("blk_copy: cannot duplicate block data");
411 memcpy(nblk
->data
, blk
->data
, blk
->maxsize
);
417 * blk_cmp - compare blocks
424 * TRUE => BLOCKs are different
425 * FALSE => BLOCKs are the same
428 blk_cmp(BLOCK
*a
, BLOCK
*b
)
431 * firewall and quick check
434 /* pointers to the same object */
437 if (a
== NULL
|| b
== NULL
) {
438 /* one pointer is NULL, so they differ */
445 if (a
->datalen
!= b
->datalen
) {
446 /* different lengths are different */
451 * compare the section
453 * We have the same lengths and types, so compare the data sections.
455 if (memcmp(a
->data
, b
->data
, a
->datalen
) != 0) {
456 /* different sections are different */
461 * the blocks are the same
468 * Print chunksize, maxsize, datalen on line line and if datalen > 0,
469 * up to * 30 octets on the following line, with ... if datalen exceeds 30.
473 blk_print(BLOCK
*blk
)
479 /* XXX - should use the config parameters for better print control */
481 printf("chunksize = %d, maxsize = %d, datalen = %d\n\t",
482 (int)blk
->blkchunk
, (int)blk
->maxsize
, (int)blk
->datalen
);
489 printf("%02x", *ptr
++);
496 * Routine to print id and name of a named block and details of its
500 nblock_print(NBLOCK
*nblk
)
504 /* XXX - use the config parameters for better print control */
507 printf("block %d: %s\n\t", nblk
->id
, nblk
->name
);
508 if (blk
->data
== NULL
) {
509 printf("chunksize = %d, maxsize = %d, datalen = %d\n\t",
510 (int)blk
->blkchunk
, (int)blk
->maxsize
, (int)blk
->datalen
);
519 * realloc a named block specified by its id. The new datalen and
520 * chunksize are specified by len >= 0 and chunk > 0. If len < 0
521 * or chunk <= 0, these values used are the current datalen and
522 * chunksize, so there is no point in calling this unless len >= 0
524 * No reallocation occurs if the new maxsize is equal to the old maxsize.
527 reallocnblock(int id
, int len
, int chunk
)
535 if (id
< 0 || id
>= nblockcount
) {
536 math_error("Bad id in call to reallocnblock");
540 blk
= nblocks
[id
]->blk
;
544 chunk
= blk
->blkchunk
;
546 chunk
= BLK_CHUNKSIZE
;
547 newsize
= (1 + len
/chunk
) * chunk
;
548 oldsize
= blk
->maxsize
;
550 if (newdata
== NULL
) {
551 newdata
= malloc(newsize
);
552 if (newdata
== NULL
) {
553 math_error("Allocation failed");
556 } else if (newsize
!= oldsize
) {
557 newdata
= realloc(blk
->data
, newsize
);
558 if (newdata
== NULL
) {
559 math_error("Reallocation failed");
563 memset(newdata
+ len
, 0, newsize
- len
);
565 blk
->maxsize
= newsize
;
567 blk
->blkchunk
= chunk
;
574 * Create and return a new namedblock with specified name, len and
578 createnblock(char *name
, int len
, int chunk
)
583 if (nblockcount
>= maxnblockcount
) {
584 if (maxnblockcount
<= 0) {
585 maxnblockcount
= NBLOCKCHUNK
;
586 nblocks
= (NBLOCK
**)malloc(NBLOCKCHUNK
*
588 if (nblocks
== NULL
) {
590 math_error("unable to malloc new named blocks");
594 maxnblockcount
+= NBLOCKCHUNK
;
595 nblocks
= (NBLOCK
**)realloc(nblocks
, maxnblockcount
*
597 if (nblocks
== NULL
) {
599 math_error("cannot malloc more named blocks");
604 if (nblockcount
== 0)
605 initstr(&nblocknames
);
606 if (findstr(&nblocknames
, name
) >= 0) {
607 math_error("Named block already exists!!!");
610 newname
= addstr(&nblocknames
, name
);
611 if (newname
== NULL
) {
612 math_error("Block name allocation failed");
616 res
= (NBLOCK
*) malloc(sizeof(NBLOCK
));
618 math_error("Named block allocation failed");
622 nblocks
[nblockcount
] = res
;
624 res
->subtype
= V_NOSUBTYPE
;
625 res
->id
= nblockcount
++;
626 res
->blk
= blkalloc(len
, chunk
);
635 findnblockid(char * name
)
637 return findstr(&nblocknames
, name
);
642 * free data block for named block with specified id
649 if (id
< 0 || id
>= nblockcount
)
652 if (nblk
->blk
->data
== NULL
)
654 if (nblk
->subtype
& V_NOREALLOC
)
656 free(nblk
->blk
->data
);
657 nblk
->blk
->data
= NULL
;
658 nblk
->blk
->maxsize
= 0;
659 nblk
->blk
->datalen
= 0;
665 * count number of current unfreed named blocks
673 for (n
= 0, id
= 0; id
< nblockcount
; id
++) {
674 if (nblocks
[id
]->blk
->data
!= NULL
)
682 * display id and name for each unfreed named block
689 if (countnblocks() == 0) {
690 printf("No unfreed named blocks\n\n");
693 printf(" id name\n");
694 printf("---- -----\n");
695 for (id
= 0; id
< nblockcount
; id
++) {
696 if (nblocks
[id
]->blk
->data
!= NULL
)
697 printf("%3d %s\n", id
, nblocks
[id
]->name
);
704 * Return pointer to nblock with specified id, NULL if never created.
705 * The memory for the nblock found may have been freed.
710 if (id
< 0 || id
>= nblockcount
)
717 * Create a new block with specified newlen and new chunksize and copy
718 * min(newlen, oldlen) octets to the new block. The old block is
722 copyrealloc(BLOCK
*blk
, int newlen
, int newchunk
)
727 oldlen
= blk
->datalen
;
729 if (newlen
< 0) /* retain length */
732 if (newchunk
< 0) /* retain chunksize */
733 newchunk
= blk
->blkchunk
;
734 else if (newchunk
== 0) /* use default chunksize */
735 newchunk
= BLK_CHUNKSIZE
;
738 newblk
= blkalloc(newlen
, newchunk
);
744 memcpy(newblk
->data
, blk
->data
, oldlen
);