modified: SpatialOmicsCoord.py
[GalaxyCodeBases.git] / c_cpp / etc / calc / block.c
blob87c6073203e64680f56fce64d829db1579fd5fea
1 /*
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/
34 #include <stdio.h>
35 #include "value.h"
36 #include "zmath.h"
37 #include "config.h"
38 #include "block.h"
39 #include "nametype.h"
40 #include "str.h"
41 #include "calcerr.h"
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
58 * given:
59 * len - initial memory length of the block
60 * type - BLK_TYPE_XYZ
61 * chunk - allocation chunk size
63 * returns:
64 * pointer to a newly allocated BLOCK
66 BLOCK *
67 blkalloc(int len, int chunk)
69 BLOCK *nblk; /* new block allocated */
72 * firewall
74 if (len < 0)
75 len = 0;
76 if (chunk <= 0)
77 chunk = BLK_CHUNKSIZE;
80 * allocate BLOCK
82 nblk = (BLOCK *)malloc(sizeof(BLOCK));
83 if (nblk == NULL) {
84 math_error("cannot allocate block");
85 /*NOTREACHED*/
89 * initialize 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");
96 /*NOTREACHED*/
98 memset(nblk->data, 0, nblk->maxsize);
99 nblk->datalen = len;
102 * return BLOCK
104 if (conf->calc_debug & CALCDBG_BLOCK) {
105 blkchk(nblk);
107 return nblk;
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.
117 * given:
118 * blk - the block to free
120 void
121 blk_free(BLOCK *blk)
123 /* free if non-NULL */
124 if (blk != NULL) {
126 /* free data storage */
127 if (blk->data != NULL) {
128 free(blk->data);
131 /* free the block */
132 free(blk);
134 return;
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()
150 * given:
151 * blk - the BLOCK to check
153 * returns:
154 * if all is ok, otherwise math_error() is called and this
155 * function does not return
157 S_FUNC void
158 blkchk(BLOCK *blk)
162 * firewall - general sanity check
164 if ((conf->calc_debug & CALCDBG_BLOCK) == 0) {
165 /* do nothing when debugging is disabled */
166 return;
168 if (blk == NULL) {
169 math_error("internal: blk ptr is NULL");
170 /*NOTREACHED*/
174 * pointers must not be NULL
176 if (blk->data == NULL) {
177 math_error("internal: blk->data ptr is NULL");
178 /*NOTREACHED*/
182 * check data lengths
184 if (blk->datalen < 0) {
185 math_error("internal: blk->datalen < 0");
186 /*NOTREACHED*/
190 * check the datalen and datalen2 values
192 if (blk->datalen < 0) {
193 math_error("internal: blk->datalen < 0");
194 /*NOTREACHED*/
196 return;
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.
215 * given:
216 * blk - old BLOCK to reallocate
217 * newlen - how much data the block holds
218 * newchunk - allocation chunk size (<0 ==> no change, 0 == default)
220 BLOCK *
221 blkrealloc(BLOCK *blk, int newlen, int newchunk)
223 USB8 *nblk; /* realloced storage */
224 int newmax; /* new maximum stoage size */
227 * firewall
229 if (conf->calc_debug & CALCDBG_BLOCK) {
230 blkchk(blk);
234 * process args
236 /* newlen < 0 means do not change the length */
237 if (newlen < 0) {
238 newlen = blk->datalen;
240 /* newchunk <= 0 means do not change the chunk size */
241 if (newchunk < 0) {
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);
255 if (nblk == NULL) {
256 math_error("cannot reallocate block storage");
257 /*NOTREACHED*/
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 */
267 blk->data = nblk;
271 * deal the case of a newlen == 0 early and return
273 if (newlen == 0) {
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);
284 } else {
285 memset(blk->data, 0, blk->maxsize);
287 blk->datalen = 0;
288 if (conf->calc_debug & CALCDBG_BLOCK) {
289 blkchk(blk);
291 return blk;
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) {
320 blkchk(blk);
322 return blk;
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.
335 * given:
336 * blk - the BLOCK to shrink
338 * returns:
339 * pointer to a newly allocated BLOCK
341 void
342 blktrunc(BLOCK *blk)
345 * firewall
347 if (conf->calc_debug & CALCDBG_BLOCK) {
348 blkchk(blk);
352 * free the old storage
354 free(blk->data);
357 * setup as a zero length fixed block
359 blk->blkchunk = 1;
360 blk->maxsize = 1;
361 blk->datalen = 0;
362 blk->data = (USB8*)malloc(1);
363 if (blk->data == NULL) {
364 math_error("cannot allocate truncated block storage");
365 /*NOTREACHED*/
367 blk->data[0] = (USB8)0;
368 if (conf->calc_debug & CALCDBG_BLOCK) {
369 blkchk(blk);
371 return;
376 * blk_copy - copy a block
378 * given:
379 * blk - the block to copy
381 * returns:
382 * pointer to copy of blk
384 BLOCK *
385 blk_copy(BLOCK *blk)
387 BLOCK *nblk; /* copy of blk */
390 * malloc new block
392 nblk = (BLOCK *)malloc(sizeof(BLOCK));
393 if (nblk == NULL) {
394 math_error("blk_copy: cannot malloc BLOCK");
395 /*NOTREACHED*/
399 * duplicate most of the block
401 *nblk = *blk;
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");
409 /*NOTREACHED*/
411 memcpy(nblk->data, blk->data, blk->maxsize);
412 return nblk;
417 * blk_cmp - compare blocks
419 * given:
420 * a first BLOCK
421 * b second BLOCK
423 * returns:
424 * TRUE => BLOCKs are different
425 * FALSE => BLOCKs are the same
428 blk_cmp(BLOCK *a, BLOCK *b)
431 * firewall and quick check
433 if (a == b) {
434 /* pointers to the same object */
435 return FALSE;
437 if (a == NULL || b == NULL) {
438 /* one pointer is NULL, so they differ */
439 return TRUE;
443 * compare lengths
445 if (a->datalen != b->datalen) {
446 /* different lengths are different */
447 return TRUE;
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 */
457 return TRUE;
461 * the blocks are the same
463 return FALSE;
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.
471 /*ARGSUSED*/
472 void
473 blk_print(BLOCK *blk)
475 long i;
476 BOOL havetail;
477 USB8 *ptr;
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);
483 i = blk->datalen;
484 havetail = (i > 30);
485 if (havetail)
486 i = 30;
487 ptr = blk->data;
488 while (i-- > 0)
489 printf("%02x", *ptr++);
490 if (havetail)
491 printf("...");
496 * Routine to print id and name of a named block and details of its
497 * block component.
499 void
500 nblock_print(NBLOCK *nblk)
502 BLOCK *blk;
504 /* XXX - use the config parameters for better print control */
506 blk = nblk->blk;
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);
511 printf("NULL");
512 } else {
513 blk_print(blk);
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
523 * and/or chunk > 0.
524 * No reallocation occurs if the new maxsize is equal to the old maxsize.
526 NBLOCK *
527 reallocnblock(int id, int len, int chunk)
529 BLOCK *blk;
530 int newsize;
531 int oldsize;
532 USB8* newdata;
534 /* Fire wall */
535 if (id < 0 || id >= nblockcount) {
536 math_error("Bad id in call to reallocnblock");
537 /*NOTREACHED*/
540 blk = nblocks[id]->blk;
541 if (len < 0)
542 len = blk->datalen;
543 if (chunk < 0)
544 chunk = blk->blkchunk;
545 else if (chunk == 0)
546 chunk = BLK_CHUNKSIZE;
547 newsize = (1 + len/chunk) * chunk;
548 oldsize = blk->maxsize;
549 newdata = blk->data;
550 if (newdata == NULL) {
551 newdata = malloc(newsize);
552 if (newdata == NULL) {
553 math_error("Allocation failed");
554 /*NOTREACHED*/
556 } else if (newsize != oldsize) {
557 newdata = realloc(blk->data, newsize);
558 if (newdata == NULL) {
559 math_error("Reallocation failed");
560 /*NOTREACHED*/
563 memset(newdata + len, 0, newsize - len);
565 blk->maxsize = newsize;
566 blk->datalen = len;
567 blk->blkchunk = chunk;
568 blk->data = newdata;
569 return nblocks[id];
574 * Create and return a new namedblock with specified name, len and
575 * chunksize.
577 NBLOCK *
578 createnblock(char *name, int len, int chunk)
580 NBLOCK *res;
581 char *newname;
583 if (nblockcount >= maxnblockcount) {
584 if (maxnblockcount <= 0) {
585 maxnblockcount = NBLOCKCHUNK;
586 nblocks = (NBLOCK **)malloc(NBLOCKCHUNK *
587 sizeof(NBLOCK *));
588 if (nblocks == NULL) {
589 maxnblockcount = 0;
590 math_error("unable to malloc new named blocks");
591 /*NOTREACHED*/
593 } else {
594 maxnblockcount += NBLOCKCHUNK;
595 nblocks = (NBLOCK **)realloc(nblocks, maxnblockcount *
596 sizeof(NBLOCK *));
597 if (nblocks == NULL) {
598 maxnblockcount = 0;
599 math_error("cannot malloc more named blocks");
600 /*NOTREACHED*/
604 if (nblockcount == 0)
605 initstr(&nblocknames);
606 if (findstr(&nblocknames, name) >= 0) {
607 math_error("Named block already exists!!!");
608 /*NOTREACHED*/
610 newname = addstr(&nblocknames, name);
611 if (newname == NULL) {
612 math_error("Block name allocation failed");
613 /*NOTREACHED*/
616 res = (NBLOCK *) malloc(sizeof(NBLOCK));
617 if (res == NULL) {
618 math_error("Named block allocation failed");
619 /*NOTREACHED*/
622 nblocks[nblockcount] = res;
623 res->name = newname;
624 res->subtype = V_NOSUBTYPE;
625 res->id = nblockcount++;
626 res->blk = blkalloc(len, chunk);
627 return res;
632 * find a named block
635 findnblockid(char * name)
637 return findstr(&nblocknames, name);
642 * free data block for named block with specified id
645 removenblock(int id)
647 NBLOCK *nblk;
649 if (id < 0 || id >= nblockcount)
650 return E_BLKFREE3;
651 nblk = nblocks[id];
652 if (nblk->blk->data == NULL)
653 return 0;
654 if (nblk->subtype & V_NOREALLOC)
655 return E_BLKFREE5;
656 free(nblk->blk->data);
657 nblk->blk->data = NULL;
658 nblk->blk->maxsize = 0;
659 nblk->blk->datalen = 0;
660 return 0;
665 * count number of current unfreed named blocks
668 countnblocks(void)
670 int n;
671 int id;
673 for (n = 0, id = 0; id < nblockcount; id++) {
674 if (nblocks[id]->blk->data != NULL)
675 n++;
677 return n;
682 * display id and name for each unfreed named block
684 void
685 shownblocks(void)
687 int id;
689 if (countnblocks() == 0) {
690 printf("No unfreed named blocks\n\n");
691 return;
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);
699 printf("\n");
704 * Return pointer to nblock with specified id, NULL if never created.
705 * The memory for the nblock found may have been freed.
707 NBLOCK *
708 findnblock(int id)
710 if (id < 0 || id >= nblockcount)
711 return NULL;
712 return nblocks[id];
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
719 * not changed.
721 BLOCK *
722 copyrealloc(BLOCK *blk, int newlen, int newchunk)
724 BLOCK * newblk;
725 int oldlen;
727 oldlen = blk->datalen;
729 if (newlen < 0) /* retain length */
730 newlen = oldlen;
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);
740 if (newlen < oldlen)
741 oldlen = newlen;
743 if (newlen > 0)
744 memcpy(newblk->data, blk->data, oldlen);
746 return newblk;