Related
I am looking for a hash function, that can hash a list of non-repeating integers while ignoring the order of them.
Example
I want the two lists
l1 = [0, 1, 3, 7]
l2 = [7, 3, 1, 0]
to have the same hash.
Background
I have an algorithm that finds a list of vertices on a graph. In an undirected graph, the algorithm will find certain lists multiple times in different orders. With my current understanding of the algorithm, it is easier to filter out the duplicates rather than re-inventing the algorithm. For performance reasons, I understand it to be easier to hash the found lists of vertices rather than comparing the whole lists.
Possible answers
Now, I see that
an XOR or a simple sum might be an answer.
Unfortunately, both offer too much potential for hash collisions, as I see it.
The not-very-efficient working method is to sort a list, and then use this sorted list to compare the new list (also sorted) against.
Other Thoughts
Given that
The lists contain only integers.
The integers will be the vertex indices, and the graph can have billions of vertices.
The integers in a list are non-repeating, and their order doesn't matter.
The lists can and will consist of between 2 and 100 (and in some cases > 1000) entries.
No need for cryptographically-secure randomness.
I have this feeling that there should be a relatively easy and straight-forward answer, and I just have not found it.
Use a combination of the product, sum and ^. All are communitive (order independent) with unsigned math.
unsigned long long product = 1;
unsigned sum = 0; // Maybe unsigned long long
unsigned x = 0;
for (i=0; i < array_element_count; i++) {
product *= l[i];
sum += l[i];
x ^= l[i];
}
unsigned long long pre_hash = product + sum + ((unsigned long long) x << 32));
unsigned hash = pre_hash % hash_table_size;
Tip: hash_table_size should be a prime to effectively use all pre_hash bits.
If array_element_count was high, I would consider p *= shift_right_until_odd(l[i]), else p will too often become 0.
If l[i] == 0 p *= l[i] deserves something different. A simple mitigation is p *= l[i] | 1, but that is something pulled out of the air.
Hashing takes time for good design and the above are candidate building blocks for OP.
Any CRC will do the job. Just XOR (I have used 64bit numbers, but 32bits crc, but it should work also with full 64 xor/crc or 32bit xor/crc) the elements together (to eliminate any order between them, as the XOR operation is conmutative, you eliminate the dependency on the order) mod 2&31, then take a CRC32 of the result (that will spread the set of values uniformly, as it warrants ---or tries to--- that a change in one bit will affect half of the bits in the result) See here for sample code and several crc tables. The repository is BSD license, so you can use it as desired.
Below is a sample implementation that generates random lists, and reorders them, comparing their hashes:
crc32ieee8023.h
#ifndef CRC32IEEE8023_H
#define CRC32IEEE8023_H
#include "crc.h"
extern CRC_STATE crc32ieee8023[];
#endif /* CRC32IEEE8023_H */
crc.h
#ifndef CRC_H
#define CRC_H
#include <stdlib.h>
#include <stdint.h>
#define CRC_TABLE_SIZE 256
#define CRC_BYTE_SIZE 8
#define CRC_BYTE_MASK 0xff
typedef uint8_t CRC_BYTE;
typedef uint64_t CRC_STATE;
CRC_STATE do_crc(
CRC_STATE state,
CRC_BYTE *buff,
size_t nbytes,
CRC_STATE *table);
#endif /* CRC_H */
test_xor_crc_hash.c
(This is the important file, where all the stuff is included.)
/* test_crc_table -- program to test a crc hash algorithm that
* checks a list of numbers and generates the same crc in a form
* that is independent on the list order presented.
* Program generates a list of random numbers (32bit) then it
* generates a random permutation of the list and a sorted list,
* calculates the hash over the three lists, and compares them.
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "crc.h"
#include "crc32ieee8023.h"
#define DFLT_N 10
#define RANDOM_DEV "/dev/urandom"
int long_compare(
const void *_a,
const void *_b);
void print(
const char *name,
const uint64_t *v,
int vsz,
CRC_STATE crc,
uint64_t xor);
int
main(int argc, char **argv)
{
int opt;
int n = DFLT_N,
res;
/* process options */
while ((opt = getopt(argc, argv, "n:")) != EOF) {
switch (opt) {
case 'n': res = sscanf(optarg, "%u", &n);
if (res != 1) {
fprintf(stderr,
"%s: invalid format (-n)\n",
optarg);
}
break;
} /* switch */
} /* while */
/* initialization of random number generator */
unsigned short random_state[3];
int fd = open(RANDOM_DEV, O_RDONLY);
if (fd < 0) {
fprintf(stderr,
"open: %s: %s\n",
RANDOM_DEV, strerror(errno));
exit(EXIT_FAILURE);
}
res = read(fd, random_state, sizeof random_state);
if (res < 0) { /* error */
fprintf(stderr,
"read: %s: %s\n",
RANDOM_DEV, strerror(errno));
exit(EXIT_FAILURE);
}
if (res < sizeof random_state) {
fprintf(stderr,
"read: %s: incomplete read (%d/%zd)\n",
RANDOM_DEV, res, sizeof random_state);
exit(EXIT_FAILURE);
}
seed48(random_state);
close(fd);
/* generate a list of random numbers and make two copies */
uint64_t *original = calloc(n, sizeof *original),
*copy_sorted = calloc(n, sizeof *copy_sorted),
*random_sort = calloc(n, sizeof *random_sort);
/* make two copies */
for (int i = 0; i < n; i++) {
original[i] = copy_sorted[i]
= random_sort[i]
= (long)lrand48() | ((long)lrand48() << 32);
}
/* sort the numbers */
qsort(copy_sorted, n, sizeof *copy_sorted, long_compare);
/* and random permutation */
for (int i = 0; i < n-1; i++) {
int j = lrand48() % (n - i);
if (i != j) {
uint64_t temp = random_sort[i];
random_sort[i] = random_sort[j];
random_sort[j] = temp;
}
}
/* calculate the sorts */
uint64_t xor_original = 0, xor_sorted = 0, xor_random = 0;
for (int i = 0; i < n; i++) {
xor_original ^= original[i];
xor_sorted ^= copy_sorted[i];
xor_random ^= random_sort[i];
}
/* now, calculate the crc's (a crc64 would be better for long) */
CRC_STATE
crc_original = do_crc(0xffffffff, (unsigned char *)&xor_original,
sizeof xor_original, crc32ieee8023),
crc_sorted = do_crc(0xffffffff, (unsigned char *)&xor_sorted,
sizeof xor_sorted, crc32ieee8023),
crc_random = do_crc(0xffffffff, (unsigned char *)&xor_random,
sizeof xor_random, crc32ieee8023);
print("original", original, n, crc_original, xor_original);
print(" sorted", copy_sorted, n, crc_sorted, xor_sorted);
print(" random", random_sort, n, crc_random, xor_random);
if (crc_original != crc_sorted || crc_sorted != crc_random) {
fprintf(stderr, "crc's don't match (crc_original == 0x%08lx, "
"crc_sorted == 0x%08lx, crc_random == 0x%08lx)\n",
crc_original, crc_sorted, crc_random);
}
/* change only one bit in one element to see how it changes the hash */
int bit_to_change = lrand48() % (n * 64),
elem_to_change = bit_to_change % n;
bit_to_change %= 64;
original[elem_to_change] ^= (1UL << bit_to_change); /* change the bit */
/* we should do the calculation over all elements, but just
* changing a bit in one element will change just the same bit in the
* xor_original accumulation variable */
uint64_t xor_original_new = xor_original;
xor_original_new ^= (1UL << bit_to_change);
printf("element=%d, bit=%d\n", elem_to_change, bit_to_change);
uint64_t crc_original_new = do_crc(0xffffffff, (unsigned char *)&xor_original_new, sizeof xor_original_new, crc32ieee8023);
print(" chg1bit", original, n, crc_original_new, xor_original_new);
}
int long_compare(const void *_a, const void *_b)
{
const uint64_t *a = _a, *b = _b;
return *a == *b
? 0
: *a > *b
? +1
: -1;
}
void print(const char *name, const uint64_t *v, int vsz, CRC_STATE crc, uint64_t xor)
{
printf("%s: { ", name);
char *sep = "";
for (int i = 0; i < vsz; i++) {
printf("%s0x%016lx", sep, v[i]);
sep = ", ";
}
printf(" }\n"
" xor = 0x%016lx, crc = 0x%08lx\n",
xor, crc);
}
crc.c
#include <sys/types.h>
#include "crc.h"
/* table based CRC calculation */
CRC_STATE do_crc(
CRC_STATE state,
CRC_BYTE *buff,
size_t nbytes,
CRC_STATE *table)
{
CRC_STATE index;
while (nbytes--) {
state ^= *buff++;
index = state & CRC_BYTE_MASK;
state >>= CRC_BYTE_SIZE;
state ^= table[index];
} /* while */
return state;
} /* do_crc */
crc32ieee8023.c
#include "crc.h"
/* variables */
CRC_STATE crc32ieee8023[] = {
/* Comando usado: mkcrc -gpedb88320 */
/* Polinomio: x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 */
/* 0 */ 0x0, 0x77073096, 0xee0e612c, 0x990951ba,
/* 4 */ 0x76dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
/* 8 */ 0xedb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
/* 12 */ 0x9b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
/* 16 */ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
/* 20 */ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
/* 24 */ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
/* 28 */ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
/* 32 */ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
/* 36 */ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
/* 40 */ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
/* 44 */ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
/* 48 */ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
/* 52 */ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
/* 56 */ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
/* 60 */ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
/* 64 */ 0x76dc4190, 0x1db7106, 0x98d220bc, 0xefd5102a,
/* 68 */ 0x71b18589, 0x6b6b51f, 0x9fbfe4a5, 0xe8b8d433,
/* 72 */ 0x7807c9a2, 0xf00f934, 0x9609a88e, 0xe10e9818,
/* 76 */ 0x7f6a0dbb, 0x86d3d2d, 0x91646c97, 0xe6635c01,
/* 80 */ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
/* 84 */ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
/* 88 */ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
/* 92 */ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
/* 96 */ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
/* 100 */ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
/* 104 */ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
/* 108 */ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
/* 112 */ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
/* 116 */ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
/* 120 */ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
/* 124 */ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
/* 128 */ 0xedb88320, 0x9abfb3b6, 0x3b6e20c, 0x74b1d29a,
/* 132 */ 0xead54739, 0x9dd277af, 0x4db2615, 0x73dc1683,
/* 136 */ 0xe3630b12, 0x94643b84, 0xd6d6a3e, 0x7a6a5aa8,
/* 140 */ 0xe40ecf0b, 0x9309ff9d, 0xa00ae27, 0x7d079eb1,
/* 144 */ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
/* 148 */ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
/* 152 */ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
/* 156 */ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
/* 160 */ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
/* 164 */ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
/* 168 */ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
/* 172 */ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
/* 176 */ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
/* 180 */ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
/* 184 */ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
/* 188 */ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
/* 192 */ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x26d930a,
/* 196 */ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x5005713,
/* 200 */ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0xcb61b38,
/* 204 */ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0xbdbdf21,
/* 208 */ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
/* 212 */ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
/* 216 */ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
/* 220 */ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
/* 224 */ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
/* 228 */ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
/* 232 */ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
/* 236 */ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
/* 240 */ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
/* 244 */ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
/* 248 */ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
/* 252 */ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
}; /* crc32ieee8023 */
Makefile
targets = test_xch
toclean = $(targets)
test_xch_deps =
test_xch_objs = crc32ieee8023.o crc.o test_xor_crc_hash.o
test_xch_libs =
test_xch_ldfl =
toclean += $(test_xch_objs)
all: $(targets)
clean:
$(RM) $(toclean)
test_xch: $(test_xch_deps) $(test_xch_objs)
$(CC) $(LDFLAGS) $($#_ldfl) -o $# $($#_objs) $($#_libs) $(LIBS)
To make the program, just run:
$ make
and to run it, you can use option -n that allows you to specify the number of random elements to generate.
I think you will have to invent one to avoid the slow sorting option. In addition to XOR and arithmetic addition, there are bit rotations, and bit masks you could use. If you need high collision resistance, you could just combine more than one of the hash functions. e.g. Assuming the d_i and arithmetic are modular like with uint32_t for example,
H_1 = sum_{i = 1 to n} d_i
H_2 = xor_{i = 1 to n} d_i
H_3 = xor_{i = 1 to n} (rotl(d_i, d_i & 0x1f) + c)
Then take H1H2H3 as a 12 byte hash.
char * lpbitmap = NULL;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
I am using the windows GDI header file to create a bitmap, the goal is to convert the lpbitmap buffer to a base64 string so that it can be represented as such data:image/bmp;base64,Qk06
as far as I understand the bitmap is 24bit where every 3 chars represent RGB values example
How to convert each RGB value into a base64 value
Update
I changed the bitcount to 24
Below is my conversion of LihO's C++ answer to C: Base64 decode snippet in C++
As I mentioned in the comments, I thought it would be simple. But,there wasn't a simple cookbook to give to someone not familiar with C++. So, to do the conversion properly, I did it myself.
And, there were a few issues:
The answer did a lot of unnecessary buffer copying.
Using std::string and std::vector, although pretty fast, are somewhat slower than using raw byte buffers.
Using base64_chars.find is O(n) but if an inverse table is created/used, it can be O(1).
The answer did not support line breaks (e.g. \n) as most base64 programs do.
Anyway, below are the source files I came up with. They can be compiled with:
cc -I. -o b64test b64test.c base64.c dbgcom.c
Note that the mmap mode (i.e. -m) is untested.
FILE: base64.h
// base64.h -- base64 definitions
#ifndef _base64_h_
#define _base64_h_
#include <dbgcom.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
typedef struct {
u32 buf_opt; // buffer options
const char *buf_tag; // buffer name
byte *buf_base; // buffer pointer
size_t buf_len; // buffer length
int buf_fdmap; // mmap file descriptor
} b64buf_t;
#define B64BUFDEF(_sym) \
b64buf_t _sym = { .buf_tag = #_sym }
typedef struct {
//u32 ctl_opt; // options
const char *ctl_tag; // control name
b64buf_t ctl_inb; // input buffer
b64buf_t ctl_out; // output buffer
int ctl_wrapnow; // last output char was newline
int ctl_wraplen; // output wrap length
int ctl_wrapmax; // output wrap max
int ctl_fdchk; // file cross check
} b64ctl_t;
#define B64CTLDEF(_sym) \
b64ctl_t _sym = { .ctl_tag = #_sym }
#define B64MMAP (1u << 0) // 1=use mmap on output
#define B64TRIM (1u << 1) // 1=trim output file
// b64encode -- encode raw data to base64
// RETURNS actual output length
size_t
b64encode(b64ctl_t *ctl);
// b64decode -- decode base64 into raw binary
// RETURNS: actual output length
size_t
b64decode(b64ctl_t *ctl);
// b64bufdup -- safe buffer duplicate
void
b64bufdup(b64buf_t *dst,const b64buf_t *src);
// b64bufput -- output buffer to file
void
b64bufput(const b64buf_t *bufp,const char *ofile);
// b64bufget -- input file to buffer
void
b64bufget(b64buf_t *bufp,const char *ifile);
// b64setup -- passive setup
void
b64setup(b64ctl_t *ctl,u32 opt,const char *tag);
// b64init_encode -- initialize for encode
// RETURNS: 0=okay, -1=error
int
b64init_encode(b64ctl_t *ctl,const b64buf_t *inb,int wrapmax);
// b64init_decode -- initialize for decode
// RETURNS: 0=okay, -1=error
int
b64init_decode(b64ctl_t *ctl,b64buf_t *inb);
// b64destroy -- destroy/deinit output buffer
void
b64destroy(b64ctl_t *ctl);
// b64bufrls -- destroy/deinit output buffer
void
b64bufrls(b64buf_t *buf,const char *who);
#endif
FILE: base64.c
// base64.c -- base 64 encoder/decoder
#include <base64.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/mman.h>
static const char *base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
#ifndef B64DECODE_FAST
#define B64DECODE_FAST 1
#endif
#if B64DECODE_FAST
static byte decode_fast[256];
#endif
#define FMT "%2.2X/%c"
#define PRT(_c) _c,PRTABLE(_c)
#define PUT4(_idx,_val) \
arr4[_idx] = _val
#define PUTE(_idx,_val) \
arr4[_idx] = base64_chars[_val]
#define OUTLEN(_buf,_ptr) \
(_ptr - (byte *) (_buf)->buf_base)
// encode_xcheck -- cross-check encoding
#if B64ENCODE_XCHECK
#define encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad) \
_encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad)
static void
_encode_xcheck(b64ctl_t *ctl,
const byte *arr4,int a4siz,
const byte *arr3,int a3siz,int pad)
{
int xlen;
int oidx;
byte buf[a4siz];
dbgprt(ZPXHOWCHR,"encode_xcheck: ENTER a4siz=%d a3siz=%d pad=%d\n",
a4siz,a3siz,pad);
xlen = read(ctl->ctl_fdchk,buf,a4siz);
if (xlen != a4siz)
sysfault("encode_xcheck: read error -- a4siz=%d xlen=%d\n",
a4siz,xlen);
// check for error/mismatch
int errany = 0;
for (oidx = 0; oidx < a4siz; ++oidx) {
errany = (arr4[oidx] != buf[oidx]);
if (errany)
break;
}
do {
// only print if verbose mode or we have an error
if (! XVERBOSE) {
if (! errany)
break;
}
int totsiz = a4siz;
if (a3siz > totsiz)
totsiz = a3siz;
for (oidx = 0; oidx < totsiz; ++oidx) {
byte bp = 0;
byte a4 = 0;
byte a3 = 0;
size_t o4off = 0;
size_t o3off = 0;
if (oidx < a4siz) {
bp = buf[oidx];
a4 = arr4[oidx];
o4off = OUTLEN(&ctl->ctl_out,arr4);
}
if (oidx < a3siz) {
a3 = arr3[oidx];
o3off = OUTLEN(&ctl->ctl_inb,arr3);
}
int errcur = (arr4[oidx] != buf[oidx]);
printf("encode_xcheck: arr=%8.8zX/",o4off);
if (pad)
printf("censored");
else
printf("%8.8zX",o3off);
printf(" oidx=%d exp=" FMT " act=" FMT " arr3=" FMT,
oidx,PRT(bp),PRT(a4),PRT(a3));
if (errany)
printf(" ANY");
if (errcur)
printf("/CUR");
printf("\n");
}
if (errany)
sysfault("encode_xcheck: fault\n");
} while (0);
dbgprt(ZPXHOWCHR,"encode_xcheck: EXIT\n");
}
#else
#define encode_xcheck(_ctl,_arr4,_a4siz,_arr3,_a3siz,_pad) \
do { } while (0)
#endif
// encode_wrap -- perform encoding and inject newline
static inline_nodebug byte *
encode_wrap(b64ctl_t *ctl,byte *arr4)
{
dbgprt(ZPXHOWCHR,"encode_wrap: ENTER ctl_wraplen=%d ctl_wrapmax=%d\n",
ctl->ctl_wraplen,ctl->ctl_wrapmax);
do {
// bug out if wrap disabled
if (ctl->ctl_wrapmax <= 0)
break;
// wait for wrap
ctl->ctl_wraplen += 4;
ctl->ctl_wrapnow = 0;
if (ctl->ctl_wraplen < ctl->ctl_wrapmax)
break;
ctl->ctl_wraplen = 0;
ctl->ctl_wrapnow = 1;
PUT4(0,'\n');
encode_xcheck(ctl,arr4,1,NULL,0,-1);
++arr4;
} while (0);
dbgprt(ZPXHOWCHR,"encode_wrap: EXIT ctl_wraplen=%d ctl_wrapnow=%d\n",
ctl->ctl_wraplen,ctl->ctl_wrapnow);
return arr4;
}
// encode_putx -- perform encoding
static inline_nodebug byte *
encode_putx(b64ctl_t *ctl,byte *arr4,const byte *arr3,int padlen)
{
dbgprt(ZPXHOWCHR,"b64encode: LOOP arr3=(" FMT " " FMT " " FMT ") padlen=%d\n",
PRT(arr3[0]),PRT(arr3[1]),PRT(arr3[2]),padlen);
PUTE(0,(arr3[0] & 0xfc) >> 2);
PUTE(1,((arr3[0] & 0x03) << 4) + ((arr3[1] & 0xf0) >> 4));
PUTE(2,((arr3[1] & 0x0f) << 2) + ((arr3[2] & 0xc0) >> 6));
PUTE(3,arr3[2] & 0x3f);
switch (padlen) {
case 2:
PUT4(2,'=');
// fall through
case 1:
PUT4(3,'=');
break;
}
encode_xcheck(ctl,arr4,4,arr3,3,padlen);
arr4 += 4;
return arr4;
}
// is_base64 -- ensure char is a valid base64 encoding char
#if ! B64DECODE_FAST
static inline int
is_base64(byte c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
#endif
// decode_find -- convert encoded base64 char into binary byte
static inline byte
decode_find(byte chr)
{
byte out;
#if B64DECODE_FAST
out = decode_fast[chr];
#else
const char *find = strchr(base64_chars,chr);
out = find - base64_chars;
#endif
dbgprt(ZPXHOWCHR,"decode_find: XLAT out=" FMT " chr=" FMT "\n",
PRT(out),PRT(chr));
if (out == 0xFF)
sysfault("b64decode: NONBASE64 chr=" FMT "\n",PRT(chr));
return out;
}
// decode_put -- convert 4 byte encoded base64 cell into 3 binary bytes
static inline_nodebug byte *
decode_put(byte *arr3,byte *arr4,int a4len)
{
int idx;
int a3len;
dbgprt(ZPXHOWCHR,"decode_put: ENTER a4len=%d\n",a4len);
// convert to binary codes
idx = 0;
for (; idx < a4len; ++idx)
arr4[idx] = decode_find(arr4[idx]);
// add zero padding
#if 0
for (; idx < 4; ++idx)
arr4[idx] = 0;
#endif
a3len = (a4len * 3) / 4;
// store 3 binary chars for 4 byte cell
arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
arr3[1] = ((arr4[1] & 0xf) << 4) + ((arr4[2] & 0x3c) >> 2);
arr3[2] = ((arr4[2] & 0x3) << 6) + arr4[3];
#if B64VERBOSE
for (idx = 0; idx < a3len; ++idx)
dbgprt(ZPXHOWCHR,"decode_put: arr3[%d]=" FMT "\n",PRT(arr3[idx]));
#endif
// advance output pointer
arr3 += a3len;
dbgprt(ZPXHOWCHR,"decode_put: EXIT a3len=%d\n",a3len);
return arr3;
}
// finish -- finish an operation
static size_t
finish(b64ctl_t *ctl,byte *out)
{
b64buf_t *buf = &ctl->ctl_out;
size_t outlen = OUTLEN(buf,out);
if (outlen > buf->buf_len)
sysfault("finish: buffer overflow -- buf_len=%zu outlen=%zu\n",
buf->buf_len,outlen);
buf->buf_len = outlen;
return outlen;
}
// b64encode -- encode raw data to base64
// RETURNS actual output length
size_t
b64encode(b64ctl_t *ctl)
{
const byte *inb = ctl->ctl_inb.buf_base;
ssize_t inblen = ctl->ctl_inb.buf_len;
byte *arr4 = ctl->ctl_out.buf_base;
int anyflg = (inblen > 0);
dbgprt(ZPXHOWB64,"b64encode: ENTER inb=%p inblen=%zu\n",inb,inblen);
for (; inblen >= 3; inblen -= 3, inb += 3) {
arr4 = encode_putx(ctl,arr4,inb,0);
arr4 = encode_wrap(ctl,arr4);
}
if (inblen) {
byte arr3[3];
u32 j = 0;
int padlen = 3 - inblen;
dbgprt(ZPXHOWB64,"b64encode: POST inblen=%zu padlen=%d\n",
inblen,padlen);
// copy over valid bytes
for (; inblen > 0; --inblen, ++j)
arr3[j] = inb[j];
// add zeroes as padding?
for (; j < 3; ++j)
arr3[j] = 0;
// add final padding
arr4 = encode_putx(ctl,arr4,arr3,padlen);
arr4 = encode_wrap(ctl,arr4);
}
// sequence should end with newline
do {
dbgprt(ZPXHOWB64,"b64encode: WRAP anyflg=%d ctl_wrapnow=%d\n",
anyflg,ctl->ctl_wrapnow);
// no newline generation
if (ctl->ctl_wrapmax <= 0)
break;
// no legit data
if (! anyflg)
break;
// don't double up newlines if we just did a newline
if (ctl->ctl_wrapnow)
break;
PUT4(0,'\n');
encode_xcheck(ctl,arr4,1,NULL,0,0);
++arr4;
} while (0);
// always add EOS
#if 0
*arr4 = 0;
#endif
size_t outlen = finish(ctl,arr4);
dbgprt(ZPXHOWB64,"b64encode: EXIT outlen=%zu\n",outlen);
return outlen;
}
// b64decode -- decode base64 into raw binary
// RETURNS: actual output length
size_t
b64decode(b64ctl_t *ctl)
{
const char *inb = (const char *) ctl->ctl_inb.buf_base;
size_t inblen = ctl->ctl_inb.buf_len;
size_t inbidx = 0;
byte *arr3 = ctl->ctl_out.buf_base;
int outidx = 0;
byte arr4[4];
dbgprt(ZPXHOWB64,"b64decode: ENTER inb=%p\n",inb);
for (inbidx = 0; inbidx < inblen; ++inbidx) {
int chr = inb[inbidx];
dbgprt(ZPXHOWCHR,"b64decode: LOOP chr=" FMT "\n",PRT(chr));
// stop when we hit a pad char
if (chr == '=')
break;
// ignore newlines
if (chr == '\n')
continue;
// not a valid char
#if ! B64DECODE_FAST
if (! is_base64(chr)) {
sysfault("b64decode: NONBASE64 chr=" FMT "\n",PRT(chr));
break;
}
#endif
arr4[outidx++] = chr;
if (outidx == 4) {
arr3 = decode_put(arr3,arr4,4);
outidx = 0;
}
}
if (outidx > 0) {
dbgprt(ZPXHOWB64,"b64decode: POST outidx=%d inblen=%zu\n",
outidx,inblen);
// fill remainder with pad/zeroes
for (inbidx = outidx; inbidx < 4; ++inbidx)
arr4[inbidx] = 0;
arr3 = decode_put(arr3,arr4,outidx);
}
size_t outlen = finish(ctl,arr3);
dbgprt(ZPXHOWB64,"b64decode: EXIT outlen=%zu\n",outlen);
return outlen;
}
// b64bufdup -- safe buffer duplicate
void
b64bufdup(b64buf_t *dst,const b64buf_t *src)
{
const char *tag = dst->buf_tag;
dbgprt(ZPXHOWB64,"b64bufdup: ENTER dst=%s/%p src=%s/%p\n",
dst->buf_tag,dst->buf_base,src->buf_tag,src->buf_base);
if (dst->buf_base != NULL)
sysfault("b64bufdup: non-null -- dst=%s buf_base=%p src=%s\n",
tag,dst->buf_base,src->buf_tag);
*dst = *src;
dst->buf_tag = tag;
dbgprt(ZPXHOWB64,"b64bufdup: EXIT\n");
}
// b64bufput -- output buffer to file
void
b64bufput(const b64buf_t *bufp,const char *ofile)
{
int fd;
b64buf_t buf_;
b64buf_t *buf = &buf_;
ssize_t xlen;
*buf = *bufp;
printf("b64bufput: %s buf_base=%p buf_len=%zu\n",
ofile,buf->buf_base,buf->buf_len);
do {
if (buf->buf_opt & B64MMAP)
break;
fd = open(ofile,O_WRONLY | O_TRUNC | O_CREAT,0644);
if (fd < 0)
sysfault("b64bufput: unable to open '%s' -- %s\n",
ofile,strerror(errno));
for (; buf->buf_len > 0;
buf->buf_len -= xlen, buf->buf_base += xlen) {
xlen = write(fd,buf->buf_base,buf->buf_len);
if (xlen < 0)
sysfault("bufput: write error -- %s\n",strerror(errno));
}
CLOSEME(fd);
} while (0);
}
// b64bufget -- input file to buffer
void
b64bufget(b64buf_t *buf,const char *ifile)
{
struct stat st;
size_t cap;
int fd;
const char *etag = NULL;
ssize_t xlen;
do {
fd = open(ifile,O_RDONLY);
if (fd < 0) {
etag = "open";
break;
}
if (buf->buf_opt & B64MMAP) {
buf->buf_fdmap = fd;
// get file size
if (fstat(fd,&st) < 0) {
etag = "fstat";
break;
}
buf->buf_len = st.st_size;
buf->buf_base = mmap(NULL,buf->buf_len,PROT_READ,
MAP_SHARED,buf->buf_fdmap,0);
if (buf->buf_base == MAP_FAILED) {
etag = "mmap";
break;
}
break;
}
cap = 0;
buf->buf_len = 0;
buf->buf_base = NULL;
while (1) {
if (buf->buf_len >= cap) {
cap += 4096;
buf->buf_base = realloc(buf->buf_base,cap + 1);
if (buf->buf_base == NULL) {
etag = "realloc";
break;
}
}
xlen = read(fd,&buf->buf_base[buf->buf_len],cap - buf->buf_len);
if (xlen < 0) {
etag = "read";
break;
}
if (xlen == 0)
break;
buf->buf_len += xlen;
}
} while (0);
if (etag != NULL)
sysfault("b64bufget: unable to %s '%s' -- %s\n",
etag,ifile,strerror(errno));
do {
if (buf->buf_opt & B64MMAP)
break;
CLOSEME(buf->buf_fdmap);
cap = buf->buf_len;
buf->buf_base[cap] = 0;
buf->buf_base = realloc(buf->buf_base,cap + 1);
if (buf->buf_base == NULL)
sysfault("b64bufget: realloc fail -- %s\n",strerror(errno));
} while (0);
printf("b64bufget: %s buf_base=%p buf_len=%zu\n",
ifile,buf->buf_base,buf->buf_len);
}
// b64setup -- passive setup
void
b64setup(b64ctl_t *ctl,u32 opt,const char *tag)
{
b64buf_t *buf;
memset(ctl,0,sizeof(*ctl));
ctl->ctl_tag = tag;
buf = &ctl->ctl_out;
buf->buf_fdmap = -1;
buf->buf_opt = opt;
buf->buf_tag = "ctl_out";
buf = &ctl->ctl_inb;
buf->buf_fdmap = -1;
buf->buf_opt = opt;
buf->buf_tag = "ctl_inb";
ctl->ctl_fdchk = -1;
}
// init_outbuf -- initialize output buffer
static int
init_outbuf(b64ctl_t *ctl,size_t outlen)
{
b64buf_t *buf = &ctl->ctl_out;
int err = -1;
dbgprt(ZPXHOWB64,"init_outbuf: ENTER ctl=%s buf=%s buf_base=%p buf_len=%zu outlen=%zu\n",
ctl->ctl_tag,buf->buf_tag,buf->buf_base,buf->buf_len,outlen);
buf->buf_len = outlen;
do {
if (! (buf->buf_opt & B64MMAP)) {
buf->buf_base = realloc(buf->buf_base,outlen);
if (buf->buf_base == NULL)
break;
err = 0;
break;
}
if (ftruncate(buf->buf_fdmap,outlen) < 0)
break;
buf->buf_base = mmap(NULL,outlen,PROT_READ | PROT_WRITE,
MAP_SHARED,buf->buf_fdmap,0);
if (buf->buf_base == MAP_FAILED) {
buf->buf_base = NULL;
break;
}
err = 0;
} while (0);
dbgprt(ZPXHOWB64,"init_outbuf: EXIT err=%d\n",err);
return err;
}
// b64init_encode -- initialize for encode
// RETURNS: 0=okay, -1=error
int
b64init_encode(b64ctl_t *ctl,const b64buf_t *inb,int wrapmax)
{
size_t outlen;
// copy in the buffer
b64bufdup(&ctl->ctl_inb,inb);
// we need for 4 bytes of output for each 3 bytes of input
outlen = inb->buf_len;
outlen *= 4;
outlen /= 3;
outlen += 4;
// space for padding
outlen += 4;
// we wrap with newline every N bytes
if (wrapmax == 0)
wrapmax = 76;
ctl->ctl_wrapmax = wrapmax;
if (wrapmax > 0)
outlen += (outlen + wrapmax) / wrapmax;
return init_outbuf(ctl,outlen);
}
#if B64DECODE_FAST
// init_fast -- initialize fast decode lookup
static void
init_fast(void)
{
const char *cp = base64_chars;
memset(decode_fast,0xFF,sizeof(decode_fast));
for (int chr = *cp; chr != 0; ++cp, chr = *cp) {
size_t off = cp - base64_chars;
decode_fast[chr] = off;
dbgprt(ZPXHOWB64,"init_fast: FASTINIT chr='%c' off=%zu\n",
chr,off);
}
}
#endif
// b64init_decode -- initialize for decode
// RETURNS: 0=okay, -1=error
int
b64init_decode(b64ctl_t *ctl,b64buf_t *inb)
{
size_t outlen;
int err;
dbgprt(ZPXHOWB64,"b64init_decode: ENTER buf_len=%zu\n",inb->buf_len);
#if B64DECODE_FAST
if (decode_fast['B'] == 0)
init_fast();
#endif
if (inb != &ctl->ctl_inb)
ctl->ctl_inb = *inb;
inb = &ctl->ctl_inb;
// caller may already know the input length, but ...
#if 0
if (inb->buf_len == 0) {
inb->buf_len = strlen(inb->buf_base);
dbgprt(ZPXHOWB64,"b64init_decode: STRLEN buf_len=%zu\n",inb->buf_len);
}
#endif
// we need for 3 bytes of output for each 4 bytes of input
outlen = inb->buf_len;
outlen *= 3;
outlen += 3;
outlen /= 4;
err = init_outbuf(ctl,outlen);
dbgprt(ZPXHOWB64,"b64init_decode: EXIT err=%d outlen=%zu\n",err,outlen);
return err;
}
// b64destroy -- destroy/deinit output buffer
void
b64destroy(b64ctl_t *ctl)
{
b64bufrls(&ctl->ctl_out,"ctl_out");
b64bufrls(&ctl->ctl_inb,"ctl_inb");
}
// b64bufrls -- destroy/deinit output buffer
void
b64bufrls(b64buf_t *buf,const char *who)
{
size_t outlen = buf->buf_len;
int err = -1;
dbgprt(ZPXHOWB64,"b64bufrls: ENTER buf_opt=%8.8X buf_base=%p outlen=%zu (from %s)\n",
buf->buf_opt,buf->buf_base,outlen,who);
do {
if (! (buf->buf_opt & B64MMAP)) {
FREEME(buf->buf_base);
err = 0;
break;
}
if (munmap(buf->buf_base,outlen) < 0)
break;
if (buf->buf_opt & B64TRIM) {
if (ftruncate(buf->buf_fdmap,outlen) < 0)
break;
}
CLOSEME(buf->buf_fdmap);
} while (0);
if (err < 0)
exit(1);
buf->buf_base = NULL;
buf->buf_len = 0;
dbgprt(ZPXHOWB64,"b64bufrls: EXIT\n");
}
FILE: b64test.c
// b64test.c -- test program for base64
#include <base64.h>
int opt_a; // exhaustive test
int opt_L; // binary file length
int opt_k; // keep temp files
int opt_m; // use mmap
u32 opt_R = 1; // random seed
int opt_T; // number of tests
int opt_W; // wrap length
int tstno = 1; // current test number
#define ALLFILES(_cmd) \
_cmd(bin) \
_cmd(encexp) \
_cmd(encact) \
_cmd(dcdexp) \
_cmd(dcdact)
#define FILEDEF(_pre) \
const char *_pre##_file = #_pre ".TMP";
ALLFILES(FILEDEF)
#define FILERM(_pre) \
unlink(_pre##_file);
// initial random binary buffer
B64BUFDEF(binbuf);
// encoding control
B64BUFDEF(encbuf);
B64CTLDEF(ctlenc);
// decoding control
B64BUFDEF(dcdbuf);
B64CTLDEF(ctldcd);
int
doexec(const char *cmd)
{
printf("DOEXEC: %s\n",cmd);
int ignflg = (cmd[0] == '!');
if (ignflg)
++cmd;
int code = system(cmd);
if (code && (! ignflg))
exit(1);
return code;
}
int
xrand(void)
{
return rand_r(&opt_R);
}
// doclean -- remove temp files
void
doclean(void)
{
do {
if (opt_k)
break;
ALLFILES(FILERM);
} while (0);
}
void
dotest(int binlen)
{
int err;
u32 opt = 0;
b64ctl_t *ctl;
b64buf_t *buf;
char cmd[1000];
printf("\n");
for (int col = 1; col <= 80; ++col)
putchar('-');
printf("\n");
dbgprt(ZPXHOWB64,"dotest: ENTER binlen=%d\n",binlen);
buf = &binbuf;
if (binlen <= 0)
buf->buf_len = -binlen;
else
buf->buf_len = xrand() % binlen;
printf("dotest: %d -- R=%u L=%8.8zX/%zu\n",
tstno,opt_R,buf->buf_len,buf->buf_len);
doclean();
if (opt_m) {
opt |= B64MMAP;
opt |= B64TRIM;
}
// NOTE: this gets freed in the b64destroy for ctlenc below
buf->buf_base = NULL;
buf->buf_base = realloc(buf->buf_base,buf->buf_len);
if (buf->buf_base == NULL)
sysfault("dotest: realloc failure -- %s\n",strerror(errno));
// get a random buffer
for (size_t bufidx = 0; bufidx < buf->buf_len; ++bufidx)
buf->buf_base[bufidx] = xrand();
// dump it for base64 program
b64bufput(buf,bin_file);
// have base64 encode it
sprintf(cmd,"base64 %s > %s",bin_file,encexp_file);
doexec(cmd);
ctl = &ctlenc;
b64setup(ctl,opt,"ctlenc");
err = b64init_encode(ctl,buf,opt_W);
if (err < 0)
sysfault("dotest: b64init_encode failure -- %s\n",strerror(errno));
#if B64ENCODE_XCHECK
ctl->ctl_fdchk = open(encexp_file,O_RDONLY);
#endif
// have our routine encode it
b64encode(ctl);
#if B64ENCODE_XCHECK
CLOSEME(ctl->ctl_fdchk);
#endif
// save it to a file
b64bufput(&ctl->ctl_out,encact_file);
b64destroy(ctl);
// compare them
sprintf(cmd,"diff -u %s %s",encexp_file,encact_file);
doexec(cmd);
// decode it
sprintf(cmd,"base64 -d %s > %s",encexp_file,dcdexp_file);
doexec(cmd);
// passive setup
ctl = &ctldcd;
b64setup(ctl,opt,"ctldcd");
// load up the encoded file into the decode input buffer
b64bufget(&dcdbuf,encact_file);
// decode the data
err = b64init_decode(ctl,&dcdbuf);
if (err < 0)
sysfault("dotest: b64init_decode failure -- %s\n",strerror(errno));
// have our routine decode it
b64decode(ctl);
b64bufput(&ctl->ctl_out,dcdact_file);
b64destroy(ctl);
// compare them
sprintf(cmd,"cmp %s %s",dcdexp_file,dcdact_file);
doexec(cmd);
// final temp file cleanup
doclean();
printf("dotest: complete\n");
dbgprt(ZPXHOWB64,"dotest: EXIT\n");
}
int
main(int argc,char **argv)
{
char *cp;
--argc;
++argv;
setlinebuf(stdout);
// use a special output directory
do {
cp = getenv("GENDIR");
if (cp == NULL)
break;
printf("GENDIR = %s\n",cp);
if (chdir(cp) < 0)
sysfault("main: chdir fault -- %s\n",strerror(errno));
} while (0);
// parse options
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'a':
opt_a = (*cp != 0) ? atoi(cp) : 1;
break;
case 'L':
opt_L = (*cp != 0) ? atoi(cp) : 1000;
break;
case 'k':
opt_k = ! opt_k;
break;
case 'm':
opt_m = ! opt_m;
break;
case 'R':
opt_R = (*cp != 0) ? atoi(cp) : 1;
break;
case 'T':
opt_T = (*cp != 0) ? atoi(cp) : 100;
break;
case 'W':
opt_W = atoi(cp);
break;
}
}
// ensure at least one test
if (opt_T <= 0)
opt_T = 1;
do {
// ensure we have a non-zero length
if (opt_L == 0)
opt_L = 100;
// do random testing
if (opt_a < 0) {
for (; tstno <= opt_T; ++tstno)
dotest(opt_L);
break;
}
// get absolute value
if (opt_L < 0)
opt_L = -opt_L;
// ensure we get at least one test
if (opt_L < opt_a)
opt_L = opt_a;
// do a range of explicit lengths
for (int binlen = opt_a; binlen <= opt_L; ++binlen, ++tstno)
dotest(-binlen);
} while (0);
printf("%d tests completed\n",tstno);
return 0;
}
FILE: dbgcom.h
// dbgflg.h -- base/debug definitions
#ifndef _dbgflg_h_
#define _dbgflg_h_
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char byte;
typedef unsigned int u32;
#define ZPXHOWALL(_cmd) \
_cmd(ZPXHOWB64) \
_cmd(ZPXHOWCHR) \
_cmd(ZPXHOWTST)
#define _DBGENUM(_sym) \
_sym,
enum {
ZPXHOWANY,
ZPXHOWALL(_DBGENUM)
ZPXHOWMAX
};
byte dbgflg[ZPXHOWMAX];
static inline byte
dbgok(int lvl)
{
return dbgflg[lvl];
}
#if DEBUG || _USE_ZPRT_
#define XVERBOSE 1
#define dbgprt(_lvl,_fmt...) \
do { \
if (! dbgok(_lvl)) \
break; \
int sverr = errno; \
printf(_fmt); \
errno = sverr; \
} while (0)
#define inline_nodebug /**/
#else
#define XVERBOSE 0
#define inline_nodebug inline
#define dbgprt(_fmt...) \
do { } while (0)
#endif
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
#define RNGE(_val,_lo,_hi) \
(((_val) >= (_lo)) && ((_val) <= (_hi)))
#define PRTABLE(_val) \
RNGE(_val,0x20,0x7E) ? (_val) : '.'
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
dbgprt(ZPXHOWB64,"CLOSEME: fd=%d (from %d)\n",_fd,__LINE__); \
close(_fd); \
_fd = -1; \
} while (0)
#define FREEME(_ptr) \
do { \
dbgprt(ZPXHOWB64,"FREEME: ptr=%p (from %d)\n",_ptr,__LINE__); \
free(_ptr); \
_ptr = NULL; \
} while (0)
#endif
FILE: dbgcom.c
// dbgcom.c -- tracing setup
#include <dbgcom.h>
struct dbgdef {
int dbg_lvl;
const char *dbg_sym;
};
#define _ZPXDEF(_sym) \
{ .dbg_lvl = _sym, .dbg_sym = #_sym },
struct dbgdef dbgdef[] = {
ZPXHOWALL(_ZPXDEF)
{ .dbg_sym = NULL }
};
void __attribute__((constructor))
dbginit(void)
{
struct dbgdef *dbg = dbgdef;
for (; dbg->dbg_sym != NULL; ++dbg) {
char *cp = getenv(dbg->dbg_sym);
if (cp == NULL)
continue;
if (! atoi(cp))
continue;
dbgflg[dbg->dbg_lvl] = 1;
dbgflg[ZPXHOWANY] = 1;
}
}
I am trying to do a simple linked-list in order to print the numbers inserted as arguments on the call of the program. However, it prints an undesirable zero on the final of the output. I guess it is a NULL that is printed, but I don't know how to get rid of it. I am still understanding the basics of linked-lists. Thank you.
/* */
#include <stdio.h>
#include <stdlib.h>
/* */
#define NUMERO_DE_ARGUMENTOS_MINIMO 3
#define EOS '\0'
/* */
#define OK 0
#define ARGUMENTO_NULO 1
#define ARGUMENTO_VAZIO 2
#define PONTEIRO_NULO 3
#define NUMERO_DE_ARGUMENTOS_INVALIDO 101
/* */
typedef struct estruturaNumeros
{
unsigned numero;
struct estruturaNumeros *proximaEstrutura;
} tipoNumeros;
/* */
int
main(int argc, char **argv)
{
/* */
tipoNumeros *numeroInicial, *proximoNumero;
char *validacao;
unsigned indiceArgumento;
/* */
numeroInicial = (tipoNumeros *) malloc(sizeof(tipoNumeros));
/* */
if (argc < NUMERO_DE_ARGUMENTOS_MINIMO)
{
printf("\n\n\nNumero de argumentos invalido.\n\n\n\n");
exit(NUMERO_DE_ARGUMENTOS_INVALIDO); /* Programa abortado. */
} /* if */
/* */
if (!numeroInicial)
{
printf("\n\n\nPonteiro nulo.\n\n\n\n");
exit(PONTEIRO_NULO); /* Programa abortado. */
} /* if */
/* */
proximoNumero = numeroInicial;
/* */
for (indiceArgumento = 1; indiceArgumento < argc; indiceArgumento++)
{
proximoNumero->numero = strtoul(*(argv + indiceArgumento), &validacao, 10);
proximoNumero->proximaEstrutura = (tipoNumeros *) malloc(sizeof(tipoNumeros));
proximoNumero = proximoNumero->proximaEstrutura;
} /* for */
/* */
proximoNumero->proximaEstrutura = NULL;
proximoNumero = numeroInicial;
/* */
printf("\n\n\n");
/* */
while (proximoNumero != NULL)
{
printf("%u\n", proximoNumero->numero);
proximoNumero = proximoNumero->proximaEstrutura;
} /* while */
/* */
printf("\n\n\n");
return OK; /* Codigo retornado com sucesso. */
} /* main */
/* output */
UBUNTU 05 --> ./exemplo_lista_encadeada_004 1 2 3
1
2
3
0
The while test should be testing the structure pointer proximaEstrutura.
Your code uses a final (or terminal) node. It is the terminal node's proximaEstrutura member that is initialised to NULL.
while (proximoNumero->proximaEstrutura != NULL)
{
printf("%u\n", proximoNumero->numero);
proximoNumero = proximoNumero->proximaEstrutura;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm currently working on a project in C which I'm required to create a buffer program. For the most part my program is finished but I'm just missing one required function: a location function to return the distance between the head (beginning of array) to the desired location designated by short loc_offset.
Given what I already have, how do I return a pointer to an index or location from my short?
Here is my given and created code:
Main (plat_bt.c):
/* File name: platy_bt.c
* Purpose:This is the main program for Assignment #1, CST8152
* Version: 1.18.1
* Author: Svillen Ranev
* Date: 16 January 2018
*/
/* The #define _CRT_SECURE_NO_WARNINGS should be used in MS Visual Studio projects
* to suppress the warnings about using "unsafe" functions like fopen()
* and standard sting library functions defined in string.h.
* The define directive does not have any effect on other compiler projects (gcc, Borland).
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include "buffer.h"
/* constant definitions */
#define INIT_CAPACITY 200 /* initial buffer capacity */
#define INC_FACTOR 15 /* increment factor */
/*check for ANSI C compliancy */
#define ANSI_C 0
#if defined(__STDC__)
#undef ANSI_C
#define ANSI_C 1
#endif
/* Declaration of an error printing function with
* variable number of arguments
*/
void err_printf(char *fmt, ...);
/* Declaration of a buffer contents display function */
void display (Buffer *ptr_Buffer);
long get_filesize(char *fname);
int main(int argc, char **argv){
pBuffer ptr_Buffer; /* pointer to Buffer structure */
FILE *fi; /* input file handle */
int loadsize = 0; /*the size of the file loaded in the buffer */
int ansi_c = !ANSI_C; /* ANSI C compliancy flag */
/* Check if the compiler option is set to compile ANSI C */
/* __DATE__, __TIME__, __LINE__, __FILE__, __STDC__ are predefined preprocessor macros*/
if(ansi_c){
err_printf("Date: %s Time: %s",__DATE__, __TIME__);
err_printf("ERROR: Compiler is not ANSI C compliant!\n");
exit(1);
}
/* missing file name or/and mode parameter */
if (argc <= 2){
err_printf("\nDate: %s Time: %s",__DATE__, __TIME__);
err_printf("\nRuntime error at line %d in file %s\n", __LINE__, __FILE__);
err_printf("%s\b\b\b\b%s%s",argv[0],": ","Missing parameters.");
err_printf("Usage: platybt source_file_name mode");
exit(1);
}
/* create a source code input buffer */
switch(*argv[2]){
case 'f': case 'a': case 'm': break;
default:
err_printf("%s%s%s",argv[0],": ","Wrong mode parameter.");
exit(1);
}
/*create the input buffer */
ptr_Buffer = b_allocate(INIT_CAPACITY,INC_FACTOR,*argv[2]);
if (ptr_Buffer == NULL){
err_printf("%s%s%s",argv[0],": ","Cannot allocate buffer.");
exit(1);
}
/* open the source file */
if ((fi = fopen(argv[1],"r")) == NULL){
err_printf("%s%s%s%s",argv[0],": ", "Cannot open file: ",argv[1]);
exit (1);
}
/* load a source file into the input buffer */
printf("Reading file %s ....Please wait\n",argv[1]);
loadsize = b_load (fi,ptr_Buffer);
if(loadsize == RT_FAIL1)
err_printf("%s%s%s",argv[0],": ","Error in loading buffer.");
/* close the source file */
fclose(fi);
/*find the size of the file */
if (loadsize == LOAD_FAIL){
printf("The input file %s %s\n", argv[1],"is not completely loaded.");
printf("Input file size: %ld\n", get_filesize(argv[1]));
}
/* display the contents of the input buffer */
display(ptr_Buffer);
/* compact the buffer
* add end-of-file character (EOF) to the buffer
* display again
*/
if(!b_compact(ptr_Buffer,EOF)){
err_printf("%s%s%s",argv[0],": ","Error in compacting buffer.");
}
display(ptr_Buffer);
/* free the dynamic memory used by the buffer */
b_free(ptr_Buffer);
/* make the buffer invalid
It is not necessary here because the function terminates anyway,
but will prevent run-time errors and crashes in future expansions
*/
ptr_Buffer = NULL;
/*return success */
return (0);
}
/* error printing function with variable number of arguments*/
void err_printf( char *fmt, ... ){
/*Initialize variable list */
va_list ap;
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
/* Move to new line */
if( strchr(fmt,'\n') == NULL )
fprintf(stderr,"\n");
}
void display (Buffer *ptr_Buffer){
printf("\nPrinting buffer parameters:\n\n");
printf("The capacity of the buffer is: %d\n",b_capacity(ptr_Buffer));
printf("The current size of the buffer is: %d\n",b_limit(ptr_Buffer));
printf("The operational mode of the buffer is: %d\n",b_mode(ptr_Buffer));
printf("The increment factor of the buffer is: %lu\n",b_incfactor(ptr_Buffer));
printf("The current mark of the buffer is: %d\n", b_mark(ptr_Buffer, b_limit(ptr_Buffer)));
/*printf("The reallocation flag is: %d\n",b_rflag(ptr_Buffer));*/
printf("\nPrinting buffer contents:\n\n");
b_rewind(ptr_Buffer);
b_print(ptr_Buffer);
}
long get_filesize(char *fname){
FILE *input;
long flength;
input = fopen(fname, "r");
if(input == NULL){
err_printf("%s%s","Cannot open file: ",fname);
return 0;
}
fseek(input, 0L, SEEK_END);
flength = ftell(input);
fclose(input);
return flength;
}
That main file has no errors or warnings, it was given to me to be used as my main.
Here is my edited source file (buffer.c):
/* File name: buffer.c
* Purpose: This is the main program for Assignment #01, CST8152
* Version: 1.0.3
* Author: Jack Loveday
* Date: 7 Febuary, 2018
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include "buffer.h"
/* b_allocate: Creates new buffer on heap
Tries to allocate memory for:
- one Buffer (calloc)
- one dynamic character buffer (char array) (malloc)
Sets buffer operational mode and inc factor
Copies given init_capacity into Buffer capacity val */
Buffer * b_allocate(short init_capacity, char inc_factor, char o_mode) {
Buffer *pBuffer;
if (init_capacity < 0 || (unsigned char)inc_factor < 0)
return NULL;
pBuffer = (Buffer *)calloc(1, sizeof(Buffer));
if (!pBuffer) {
free(pBuffer);
return NULL;
}
pBuffer->cb_head = (char *)malloc(init_capacity);
if (o_mode == 'f' || (unsigned char)inc_factor == 0) {
pBuffer->mode = 0;
pBuffer->inc_factor = 0;
}
else if (o_mode == 'a') {
pBuffer->mode = ADD_MODE;
pBuffer->inc_factor = (unsigned char)inc_factor;
}
else if (o_mode == 'm' && (unsigned char)inc_factor <= 100) {
pBuffer->mode = MULTI_MODE;
pBuffer->inc_factor = (unsigned char)inc_factor;
}
else {
free(pBuffer->cb_head);
free(pBuffer);
return NULL;
}
pBuffer->capacity = init_capacity;
return pBuffer;
}
/* b_addc: resets r_flag to 0 & tries to add symbol to char array pointed by pBD */
pBuffer b_addc(pBuffer const pBD, char symbol) {
char *tBuffer;
short aSpace = 0, newInc = 0, newCapacity = 0;
if (!pBD)
return NULL;
pBD->r_flag = 0;
if ((short)(pBD->addc_offset * sizeof(char)) < pBD->capacity) {
pBD->cb_head[pBD->addc_offset] = symbol;
pBD->addc_offset++;
}
else if ((short)(pBD->addc_offset * sizeof(char)) == pBD->capacity) {
if (pBD->mode == 0)
return NULL;
else if (pBD->mode == 1) {
newCapacity = pBD->capacity + (unsigned char)pBD->inc_factor * sizeof(char);
if (newCapacity <= 0)
return NULL;
pBD->capacity = newCapacity;
}
else if (pBD->mode == -1) {
if (pBD->capacity == SHRT_MAX)
return NULL;
else {
aSpace = SHRT_MAX - pBD->capacity;
newInc = (short)((aSpace * pBD->inc_factor) / 100);
if (!newInc)
newInc = NEW_ONE;
newCapacity = (short)((pBD->capacity + newInc));
if (newCapacity <= 0)
newCapacity = SHRT_MAX;
}
}
tBuffer = (char *)realloc(pBD->cb_head, newCapacity);
if (tBuffer != pBD->cb_head)
pBD->r_flag = SET_R_FLAG;
pBD->cb_head = tBuffer;
pBD->cb_head[pBD->addc_offset] = symbol;
pBD->addc_offset++;
pBD->capacity = newCapacity;
}
return pBD;
}
/* b_clear: retains memory space currently allocated to buffer, but
re-initializes all appropriate members of given Buffer */
int b_clear(Buffer * const pBD){
if (!pBD)
return RT_FAIL1;
pBD->capacity = 0;
pBD->addc_offset = 0;
pBD->getc_offset = 0;
pBD->eob = 0;
return 1;
}
/* b_free: de-allocates memory for char array & Buffer struct */
void b_free(Buffer * const pBD) {
if (pBD) {
free(pBD->cb_head);
free(pBD);
}
}
/* b_isfull: returns 1 if buffer is full, otherwise returns 0, if rt error return -1 */
int b_isfull(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
if ((short)(pBD->addc_offset * sizeof(char)) == pBD->capacity)
return 1;
else
return 0;
}
/* b_limit: returns current limit of buffer, return -1 on error */
short b_limit(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
return pBD->addc_offset;
}
/* b_capacity: returns current capacity of buffer, return -1 on error */
short b_capacity(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
return pBD->capacity;
}
/* b_mark: sets markc_offset to mark, must be between 0 and addc_offset, return -1 on error */
short b_mark(pBuffer const pBD, short mark) {
if (!pBD || mark < 0 || mark > pBD->addc_offset)
return RT_FAIL1;
pBD->markc_offset = mark;
return pBD->markc_offset;
}
/* b_mode: returns value of mode, or -1 on error */
int b_mode(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
return pBD->mode;
}
/* b_infactor: returns non-negative value of inc_factor, return 0x100 (256) */
size_t b_incfactor(Buffer * const pBD) {
if (!pBD)
return NEW_FULL;
return (unsigned char)pBD->inc_factor;
}
/* b_load: returns value of mode */
int b_load(FILE * const fi, Buffer * const pBD) {
char c;
int cNum = 0;
if (!fi || !pBD)
return RT_FAIL1;
for (;;) {
c = (char)fgetc(fi);
if (feof(fi))
break;
if (!b_addc(pBD, c))
return LOAD_FAIL;
++cNum;
}
fclose(fi);
return cNum;
}
/* b_isempty: if addc_offset is 0 return 1, otherwise return 0, return -1 on error */
int b_isempty(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
if (!pBD->addc_offset)
return 1;
else
return 0;
}
/* b_eob: returns eob (end of buffer), returns -1 on error */
int b_eob(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
return pBD->eob;
}
/* b_getc: checks logic, if not valid returns -2, if getc_offset and addc_offset
are equal, set eob to 1 and returns -1, otherwise set eob to 0 */
char b_getc(Buffer * const pBD) {
char returner;
if (!pBD || !pBD->cb_head)
return RT_FAIL2;
if (pBD->getc_offset == pBD->addc_offset) {
pBD->eob = 1;
return RT_FAIL1;
}
else {
pBD->eob = 0;
}
returner = *(pBD->cb_head + pBD->getc_offset);
pBD->getc_offset++;
return returner;
}
/* b_print: used for diagnostic purposes only, returns -1 on error */
int b_print(Buffer * const pBD) {
int numOfChars = 0;
char c;
if (!pBD->addc_offset) {
printf("The Buffer is empty.\n");
return numOfChars;
}
pBD->getc_offset = 0;
while (1) {
c = b_getc(pBD);
if (b_eob(pBD))
break;
printf("%c", c);
++numOfChars;
}
numOfChars = pBD->getc_offset;
pBD->getc_offset = 0;
printf("\n");
return numOfChars;
}
/* b_compact: shrinks buffer to new capacity, before returning to a pointer add symbol
to the end of buffer, must set r_flag appropriatley */
Buffer * b_compact(Buffer * const pBD, char symbol) {
char *tempBuffer;
short tempCap;
if (!pBD)
return NULL;
tempCap = (pBD->addc_offset + 1) * sizeof(char);
if (tempCap == SHRT_MAX)
return pBD;
tempBuffer = (char *)realloc(pBD->cb_head, (unsigned short)tempCap);
if (tempCap == 0)
return NULL;
if (tempBuffer != pBD->cb_head)
pBD->r_flag = SET_R_FLAG;
pBD->cb_head = tempBuffer;
pBD->capacity = tempCap;
return pBD;
}
/* b_rflag: returns r_flag, returns -1 on error */
char b_rflag(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
return pBD->r_flag;
}
/* b_retract: decrements getc_offset by 1, returns -1 on error, otherwise returns getc_offset */
short b_retract(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
pBD->getc_offset--;
return pBD->getc_offset;
}
/* b_reset: sets getc_offset to current markc_offset, returns -1 on error and getc_offset otherwise */
short b_reset(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
pBD->getc_offset = pBD->markc_offset;
return pBD->getc_offset;
}
/* b_getcoffset: returns getc_offset, or returns -1 on error */
short b_getcoffset(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
return pBD->getc_offset;
}
/* b_rewind: set getc_offset and markc_offset to 0, return -1 on error and 0 otherwise */
int b_rewind(Buffer * const pBD) {
if (!pBD)
return RT_FAIL1;
pBD->addc_offset = 0;
pBD->markc_offset = 0;
return 0;
}
/* b_location: returns a pointer to location indicated by loc_offset, returns null on error */
char * b_location(Buffer * const pBD, short loc_offset) {
if (!pBD)
return NULL;
return *loc_offset;
}
And finally my buffer.h:
/*File Name: buffer.h
* Version: 1.18.1
* Author: S^R
* Date: 16 January 2018
* Preprocessor directives, type declarations and prototypes necessary for buffer implementation
* as required for CST8152-Assignment #1.
* The file is not completed.
* You must add your function declarations (prototypes).
* You must also add your constant definitions and macros,if any.
*/
#ifndef BUFFER_H_
#define BUFFER_H_
/*#pragma warning(1:4001) *//*to enforce C89 type comments - to make //comments an warning */
/* standard header files */
#include <stdio.h> /* standard input/output */
#include <malloc.h> /* for dynamic memory allocation*/
#include <limits.h> /* implementation-defined data type ranges and limits */
/* constant definitions */
/* You may add your own constant definitions here */
#define RT_FAIL1 -1 /* fail return value */
#define RT_FAIL2 -2 /* fail return value */
#define LOAD_FAIL -2 /* load fail error */
#define SET_R_FLAG 1 /* realloc flag set value */
#define ADD_MODE 1; /* named constant for additive mode */
#define MULTI_MODE -1; /* named constant for multiplicative mode */
#define NEW_ONE 1; /* generic named constant value of 1 */
#define NEW_FULL 256; /* generic named constant value of 256 */
/* user data type declarations */
typedef struct BufferDescriptor {
char *cb_head; /* pointer to the beginning of character array (character buffer) */
short capacity; /* current dynamic memory size (in bytes) allocated to character buffer */
short addc_offset; /* the offset (in chars) to the add-character location */
short getc_offset; /* the offset (in chars) to the get-character location */
short markc_offset; /* the offset (in chars) to the mark location */
char inc_factor; /* character array increment factor */
char r_flag; /* reallocation flag */
char mode; /* operational mode indicator*/
int eob; /* end-of-buffer flag */
} Buffer, *pBuffer;
/* function declarations */
/*
Place your function declarations here.
Do not include the function header comments here.
Place them in the buffer.c file
*/
Buffer * b_allocate(short init_capacity, char inc_factor, char o_mode);
pBuffer b_addc(pBuffer const pBD, char symbol);
int b_clear(Buffer * const pBD);
void b_free(Buffer * const pBD);
int b_isfull(Buffer * const pBD);
short b_limit(Buffer * const pBD);
short b_capacity(Buffer * const pBD);
short b_mark(pBuffer const pBD, short mark);
int b_mode(Buffer * const pBD);
size_t b_incfactor(Buffer * const pBD);
int b_load(FILE * const fi, Buffer * const pBD);
int b_isempty(Buffer * const pBD);
int b_eob(Buffer * const pBD);
char b_getc(Buffer * const pBD);
int b_print(Buffer * const pBD);
Buffer * b_compact(Buffer * const pBD, char symbol);
char b_rflag(Buffer * const pBD);
short b_retract(Buffer * const pBD);
short b_reset(Buffer * const pBD);
short b_getcoffset(Buffer * const pBD);
int b_rewind(Buffer * const pBD);
char * b_location(Buffer * const pBD, short loc_offset);
#endif
So again, I need to find the location of loc_offset in my b_location function and return it as a pointer. Thanks in advance!
char * b_location(Buffer * const pBD, short loc_offset) {
if (!pBD)
return NULL;
/* Make sure loc_offset doesn't go beyond the length of the array */
if( ! in_bounds )
return NULL;
return &(pBD->cb_head[loc_offset]);
}
Assume that there are flag definitions such as:
SHF_WRITE 0x1
SHF_ALLOC 0x2
SHF_EXECINSTR 0x4
SHF_MASKPROC 0xf0000000
Given a flag, I need to output SHF_WRITE|SHF_ALLOC if the bits 0x1 and 0x2 is on.
How to do the trick in C?
#define V(n) { n, #n }
struct Code {
int value;
char *name;
} decode[] = {
V(SHF_WRITE),
V(SHF_ALLOC),
{ 0, NULL },
};
void f(const int x) {
struct Code *c = decode;
int beenthere = 0;
for(; c->name; ++c)
if(x & c->value)
printf("%s%s", beenthere++ ? "|" : "", c->name);
if(beenthere)
printf("\n");
}
Just create a character buffer with enough space to hold all possible combinations of strings and add to it the appropriate strings for each applicable bit set. (or you could ditch the buffer and write straight to stdout, your choice) Here's a naive implementation of how you could do such a thing:
void print_flags(int flag)
{
#define BUFLEN (9+9+13+12+3+1)
/* for the text, pipes and null terminator*/
#define PAIRLEN 4
static struct { int value; const char *string; } pair[] =
{
{ SHF_WRITE, "SHF_WRITE" },
{ SHF_ALLOC, "SHF_ALLOC" },
{ SHF_EXECINSTR, "SHF_EXECINSTR" },
{ SHF_MASKPROC, "SHF_MASKPROC" },
};
char buf[BUFLEN]; /* declare the buffer */
char *write = buf; /* and a "write" pointer */
int i;
for (i = 0; i < PAIRLEN; i++)
{
if ((flag & pair[i].value) == pair[i].value) /* if flag is set... */
{
size_t written = write - buf;
write += _snprintf(write, BUFLEN-written, "%s%s",
written > 0 ? "|" : "",
pair[i].string); /* write to the buffer */
}
}
if (write != buf) /* if any of the flags were set... */
{
*write = '\0'; /* null terminate (just in case) */
printf("(%s)", buf); /* print out the buffer */
}
#undef PAIRLEN
#undef BUFLEN
}
PROBLEM:
"SHF_WRITE|SHF_ALLOC" says "bit 0x1 OR bit 0x2", not "bits 0x1 AND 02x".
Nevertheless, if you wanted to print "SOME MSG" if the bits 0x1 and 0x2 were both "on" in some value "flag", here's how:
if (flag & SHF_WRITE & SHF_ALLOC)
printf ("SOME MSG, flag= 0x%x\n", flag);
If you wanted to print the text represtation of ANY bits that were "on" in the value, you might do something like this:
char buf[80] = '\0';
if (flag & SHF_WRITE)
strcpy (buf, " SHF_WRITE");
if (flag & SHF_ALLOC)
strcpy (buf, " SHF_ALLOC");
...
printf ("SOME MSG, flag= %s\n", buf);
And finally, if you DON'T want to print if NO bit is set, just do this:
if (flag)
{
... do printing ...
}
else
{
... do nothing? ...
}