1 /*  =========================================================================
2     zchunk - work with memory chunks
3 
4     Copyright (c) the Contributors as noted in the AUTHORS file.
5     This file is part of CZMQ, the high-level C binding for 0MQ:
6     http://czmq.zeromq.org.
7 
8     This Source Code Form is subject to the terms of the Mozilla Public
9     License, v. 2.0. If a copy of the MPL was not distributed with this
10     file, You can obtain one at http://mozilla.org/MPL/2.0/.
11     =========================================================================*/
12 
13 /*
14 @header
15     The zchunk class works with variable sized blobs. Not as efficient as
16     ZeroMQ's messages but they do less weirdness and so are easier to understand.
17     The chunk class has methods to read and write chunks from disk.
18 @discuss
19 @end
20 */
21 
22 #include "czmq_classes.h"
23 
24 //  zchunk_t instances always have this tag as the first 4 octets of
25 //  their data, which lets us do runtime object typing & validation.
26 #define ZCHUNK_TAG              0xcafe0001
27 
28 //  Structure of our class
29 
30 struct _zchunk_t {
31     uint32_t tag;                       //  Object tag for runtime detection
32     size_t size;                        //  Current size of data part
33     size_t max_size;                    //  Maximum allocated size
34     size_t consumed;                    //  Amount already consumed
35     zdigest_t *digest;                  //  Chunk digest, if known
36     byte *data;                         //  Data part follows here
37     zchunk_destructor_fn *destructor;   //  Destructor for memory
38     void * hint;                        //  Hint for destroying the memory
39 };
40 
41 
42 //  --------------------------------------------------------------------------
43 //  Create a new chunk of the specified size. If you specify the data, it
44 //  is copied into the chunk. If you do not specify the data, the chunk is
45 //  allocated and left empty, and you can then add data using zchunk_append.
46 
47 zchunk_t *
zchunk_new(const void * data,size_t size)48 zchunk_new (const void *data, size_t size)
49 {
50     //  Use malloc, not zmalloc, to avoid nullification costs
51     zchunk_t *self = (zchunk_t *) malloc (sizeof (zchunk_t) + size);
52     //  Catch memory exhaustion in this specific class
53     if (self) {
54         self->tag = ZCHUNK_TAG;
55         self->size = 0;
56         self->max_size = size;
57         self->consumed = 0;
58         self->data = (byte *) self + sizeof (zchunk_t);
59         self->digest = NULL;
60         self->destructor = NULL;
61         self->hint = NULL;
62         if (data) {
63             self->size = size;
64             memcpy (self->data, data, self->size);
65         }
66     }
67     return self;
68 }
69 
70 
71 //  --------------------------------------------------------------------------
72 //  Destroy a chunk
73 
74 void
zchunk_destroy(zchunk_t ** self_p)75 zchunk_destroy (zchunk_t **self_p)
76 {
77     assert (self_p);
78     if (*self_p) {
79         zchunk_t *self = *self_p;
80         assert (zchunk_is (self));
81         //  If data was reallocated independently, free it independently
82         if (self->destructor) {
83             self->destructor (&self->hint);
84             self->destructor = NULL;
85         }
86         else
87         if (self->data != (byte *) self + sizeof (zchunk_t))
88             freen (self->data);
89         self->tag = 0xDeadBeef;
90         zdigest_destroy (&self->digest);
91         freen (self);
92         *self_p = NULL;
93     }
94 }
95 
96 
97 //  --------------------------------------------------------------------------
98 //  Create a new chunk from memory. Taking ownership of the memory and calling the destructor
99 //  on destroy.
100 
101 zchunk_t *
zchunk_frommem(void * data,size_t size,zchunk_destructor_fn destructor,void * hint)102 zchunk_frommem (void *data, size_t size, zchunk_destructor_fn destructor, void *hint) {
103     assert (data);
104 
105     zchunk_t *self = (zchunk_t *) zmalloc (sizeof (zchunk_t));
106     assert (self);
107 
108     self->tag = ZCHUNK_TAG;
109     self->size = size;
110     self->max_size = size;
111     self->consumed = 0;
112     self->data = (byte *)data;
113     self->digest = NULL;
114     self->destructor = destructor;
115     self->hint = hint;
116 
117     return self;
118 }
119 
120 
121 //  --------------------------------------------------------------------------
122 //  Resizes chunk max_size as requested; chunk size is set to zero
123 
124 void
zchunk_resize(zchunk_t * self,size_t size)125 zchunk_resize (zchunk_t *self, size_t size)
126 {
127     assert (self);
128     assert (zchunk_is (self));
129     zdigest_destroy (&self->digest);
130 
131     //  Set new sizes
132     self->max_size = size;
133     self->size = 0;             //  TODO: this is a bit annoying, is it needed?
134 
135     //  If external allocated, free it first and reallocate
136     if (self->destructor) {
137         self->destructor (&self->hint);
138         self->destructor = NULL;
139         self->data = (byte *) malloc (self->max_size);
140     }
141     else
142     //  We can't realloc the chunk itself, as the caller's reference
143     //  won't change. So we modify self->data only, depending on whether
144     //  it was already reallocated, or not.
145     if (self->data == (byte *) self + sizeof (zchunk_t))
146         self->data = (byte *) malloc (self->max_size);
147     else
148         self->data = (byte *) realloc (self->data, self->max_size);
149 }
150 
151 
152 //  --------------------------------------------------------------------------
153 //  Return chunk current size
154 
155 size_t
zchunk_size(zchunk_t * self)156 zchunk_size (zchunk_t *self)
157 {
158     assert (self);
159     assert (zchunk_is (self));
160     return self->size;
161 }
162 
163 
164 //  --------------------------------------------------------------------------
165 //  Return chunk max size
166 
167 size_t
zchunk_max_size(zchunk_t * self)168 zchunk_max_size (zchunk_t *self)
169 {
170     assert (self);
171     assert (zchunk_is (self));
172     return self->max_size;
173 }
174 
175 
176 //  --------------------------------------------------------------------------
177 //  Return chunk data
178 
179 byte *
zchunk_data(zchunk_t * self)180 zchunk_data (zchunk_t *self)
181 {
182     assert (self);
183     assert (zchunk_is (self));
184     return self->data;
185 }
186 
187 
188 //  --------------------------------------------------------------------------
189 //  Set chunk data from user-supplied data; truncate if too large. Data may
190 //  be null. Returns actual size of chunk
191 
192 size_t
zchunk_set(zchunk_t * self,const void * data,size_t size)193 zchunk_set (zchunk_t *self, const void *data, size_t size)
194 {
195     assert (self);
196     assert (zchunk_is (self));
197     zdigest_destroy (&self->digest);
198 
199     if (size > self->max_size)
200         size = self->max_size;
201     if (data)
202         memcpy (self->data, data, size);
203     self->size = size;
204     return size;
205 }
206 
207 
208 //  --------------------------------------------------------------------------
209 //  Fill chunk data from user-supplied octet
210 //  Returns actual size of chunk
211 
212 size_t
zchunk_fill(zchunk_t * self,byte filler,size_t size)213 zchunk_fill (zchunk_t *self, byte filler, size_t size)
214 {
215     assert (self);
216     assert (zchunk_is (self));
217     zdigest_destroy (&self->digest);
218 
219     if (size > self->max_size)
220         size = self->max_size;
221 
222     memset (self->data, filler, size);
223     self->size = size;
224     return size;
225 }
226 
227 
228 //  --------------------------------------------------------------------------
229 //  Append user-supplied data to chunk, return resulting chunk size. If the
230 //  data would exceeded the available space, it is truncated. If you want to
231 //  grow the chunk to accommodate new data, use the zchunk_extend method.
232 
233 size_t
zchunk_append(zchunk_t * self,const void * data,size_t size)234 zchunk_append (zchunk_t *self, const void *data, size_t size)
235 {
236     assert (self);
237     assert (zchunk_is (self));
238     zdigest_destroy (&self->digest);
239 
240     if (self->size + size > self->max_size)
241         size = self->max_size - self->size;
242 
243     memcpy (self->data + self->size, data, size);
244     self->size += size;
245     return self->size;
246 }
247 
248 
249 //  --------------------------------------------------------------------------
250 //  Append user-supplied data to chunk, return resulting chunk size. If the
251 //  data would exceeded the available space, the chunk grows in size.
252 
253 size_t
zchunk_extend(zchunk_t * self,const void * data,size_t size)254 zchunk_extend (zchunk_t *self, const void *data, size_t size)
255 {
256     assert (self);
257     if (self->size + size > self->max_size) {
258         self->max_size = (self->size + size) * 2;
259 
260         //  If data was externally allocated, destroy it
261         if (self->destructor) {
262             byte *old_data = self->data;
263             self->data = (byte *) malloc (self->max_size);
264             memcpy (self->data, old_data, self->size);
265 
266             //  Release the old data
267             self->destructor (&self->hint);
268             self->destructor = NULL;
269         }
270         else
271         //  We can't realloc the chunk itself, as the caller's reference
272         //  won't change. So we modify self->data only, depending on whether
273         //  it was already reallocated, or not.
274         if (self->data == (byte *) self + sizeof (zchunk_t)) {
275             byte *old_data = self->data;
276             self->data = (byte *) malloc (self->max_size);
277             memcpy (self->data, old_data, self->size);
278         }
279         else
280             self->data = (byte *) realloc (self->data, self->max_size);
281     }
282     assert (self->size + size <= self->max_size);
283     memcpy (self->data + self->size, data, size);
284     self->size += size;
285     return self->size;
286 }
287 
288 
289 //  --------------------------------------------------------------------------
290 //  Copy as much data from 'source' into the chunk as possible; returns the
291 //  new size of chunk. If all data from 'source' is used, returns exhausted
292 //  on the source chunk. Source can be consumed as many times as needed until
293 //  it is exhausted. If source was already exhausted, does not change chunk.
294 
295 size_t
zchunk_consume(zchunk_t * self,zchunk_t * source)296 zchunk_consume (zchunk_t *self, zchunk_t *source)
297 {
298     assert (self);
299     assert (zchunk_is (self));
300     assert (source);
301     assert (zchunk_is (source));
302 
303     //  We can take at most this many bytes from source
304     size_t size = source->size - source->consumed;
305 
306     //  And we can store at most this many bytes in chunk
307     if (self->size + size > self->max_size)
308         size = self->max_size - self->size;
309 
310     memcpy (self->data + self->size, source->data + source->consumed, size);
311     source->consumed += size;
312     self->size += size;
313     return self->size;
314 }
315 
316 
317 //  --------------------------------------------------------------------------
318 //  Returns true if the chunk was exhausted by consume methods, or if the
319 //  chunk has a size of zero.
320 
321 bool
zchunk_exhausted(zchunk_t * self)322 zchunk_exhausted (zchunk_t *self)
323 {
324     assert (self);
325     assert (zchunk_is (self));
326 
327     assert (self->consumed <= self->size);
328     return self->consumed == self->size;
329 }
330 
331 
332 //  --------------------------------------------------------------------------
333 //  Read chunk from an open file descriptor
334 
335 zchunk_t *
zchunk_read(FILE * handle,size_t bytes)336 zchunk_read (FILE *handle, size_t bytes)
337 {
338     assert (handle);
339 
340     zchunk_t *self = zchunk_new (NULL, bytes);
341     if (self)
342         self->size = fread (self->data, 1, bytes, handle);
343     return self;
344 }
345 
346 
347 //  --------------------------------------------------------------------------
348 //  Write chunk to an open file descriptor
349 
350 int
zchunk_write(zchunk_t * self,FILE * handle)351 zchunk_write (zchunk_t *self, FILE *handle)
352 {
353     assert (self);
354     assert (zchunk_is (self));
355 
356     size_t items = fwrite (self->data, 1, self->size, handle);
357     int rc = (items < self->size)? -1: 0;
358     return rc;
359 }
360 
361 
362 //  --------------------------------------------------------------------------
363 //  Try to slurp an entire file into a chunk. Will read up to maxsize of
364 //  the file. If maxsize is 0, will attempt to read the entire file and
365 //  fail with an assertion if that cannot fit into memory. Returns a new
366 //  chunk containing the file data, or NULL if the file could not be read.
367 
368 zchunk_t *
zchunk_slurp(const char * filename,size_t maxsize)369 zchunk_slurp (const char *filename, size_t maxsize)
370 {
371     size_t size = zsys_file_size (filename);
372     if ((ssize_t) size == -1)
373         return NULL;
374 
375     if (size > maxsize && maxsize != 0)
376         size = maxsize;
377 
378     FILE *handle = fopen (filename, "r");
379     if (!handle)
380         return NULL;
381 
382     zchunk_t *chunk = zchunk_read (handle, size);
383     assert (chunk);
384     fclose (handle);
385     return chunk;
386 }
387 
388 
389 //  --------------------------------------------------------------------------
390 //  Create copy of chunk, as new chunk object. Returns a fresh zchunk_t
391 //  object, or null if there was not enough heap memory. If chunk is null,
392 //  or memory was exhausted, returns null.
393 
394 zchunk_t *
zchunk_dup(zchunk_t * self)395 zchunk_dup (zchunk_t *self)
396 {
397     if (self) {
398         assert (zchunk_is (self));
399         return zchunk_new (self->data, self->max_size);
400     }
401     else
402         return NULL;
403 }
404 
405 
406 //  --------------------------------------------------------------------------
407 //  Return chunk data encoded as printable hex string. Caller must free
408 //  string when finished with it.
409 
410 char *
zchunk_strhex(zchunk_t * self)411 zchunk_strhex (zchunk_t *self)
412 {
413     assert (self);
414     assert (zchunk_is (self));
415 
416     static const char
417         hex_char [] = "0123456789ABCDEF";
418 
419     size_t size = zchunk_size (self);
420     byte *data = zchunk_data (self);
421     char *hex_str = (char *) zmalloc (size * 2 + 1);
422     if (!hex_str)
423         return NULL;
424 
425     uint byte_nbr;
426     for (byte_nbr = 0; byte_nbr < size; byte_nbr++) {
427         hex_str [byte_nbr * 2 + 0] = hex_char [data [byte_nbr] >> 4];
428         hex_str [byte_nbr * 2 + 1] = hex_char [data [byte_nbr] & 15];
429     }
430     hex_str [size * 2] = 0;
431     return hex_str;
432 }
433 
434 
435 //  --------------------------------------------------------------------------
436 //  Return chunk data copied into freshly allocated string
437 //  Caller must free string when finished with it.
438 
439 char *
zchunk_strdup(zchunk_t * self)440 zchunk_strdup (zchunk_t *self)
441 {
442     assert (self);
443     assert (zchunk_is (self));
444 
445     size_t size = zchunk_size (self);
446     char *string = (char *) malloc (size + 1);
447     if (string) {
448         memcpy (string, zchunk_data (self), size);
449         string [size] = 0;
450     }
451     return string;
452 }
453 
454 
455 //  --------------------------------------------------------------------------
456 //  Return true if chunk body is equal to string, excluding terminator
457 
458 bool
zchunk_streq(zchunk_t * self,const char * string)459 zchunk_streq (zchunk_t *self, const char *string)
460 {
461     assert (self);
462     assert (zchunk_is (self));
463 
464     if (zchunk_size (self) == strlen (string)
465     &&  memcmp (zchunk_data (self), string, strlen (string)) == 0)
466         return true;
467     else
468         return false;
469 }
470 
471 
472 //  --------------------------------------------------------------------------
473 //  Create a zframe from a zchunk.  The zframe can be sent in a message.
474 
475 zframe_t *
zchunk_pack(zchunk_t * self)476 zchunk_pack (zchunk_t *self)
477 {
478     assert (self);
479     assert (zchunk_is (self));
480     return zframe_new (self->data, self->size);
481 }
482 
483 
484 //  --------------------------------------------------------------------------
485 //  Create a zchunk from a zframe.
486 
487 zchunk_t *
zchunk_unpack(zframe_t * frame)488 zchunk_unpack (zframe_t *frame)
489 {
490     assert (frame);
491     assert (zframe_is (frame));
492     return zchunk_new (zframe_data (frame), zframe_size (frame));
493 }
494 
495 
496 //  --------------------------------------------------------------------------
497 //  Transform zchunk into a zframe that can be sent in a message.
498 //  Take ownership of the chunk.
499 //  Caller owns return value and must destroy it when done.
500 
501 zframe_t *
zchunk_packx(zchunk_t ** self_p)502 zchunk_packx (zchunk_t **self_p) {
503     assert (self_p);
504     assert (*self_p);
505     zchunk_t *self = *self_p;
506     *self_p = NULL;
507 
508     return zframe_frommem (self->data, self->size, (zchunk_destructor_fn *) zchunk_destroy, self);
509 }
510 
511 
512 //  --------------------------------------------------------------------------
513 //  Calculate SHA1 digest for chunk, using zdigest class. Caller should not
514 //  modify digest.
515 
516 const char *
zchunk_digest(zchunk_t * self)517 zchunk_digest (zchunk_t *self)
518 {
519     assert (self);
520     if (!self->digest)
521         self->digest = zdigest_new ();
522     if (self->digest) {
523         zdigest_update (self->digest, self->data, self->size);
524         return zdigest_string (self->digest);
525     }
526     else
527         return NULL;
528 }
529 
530 
531 //  --------------------------------------------------------------------------
532 //  Dump chunk to FILE stream, for debugging and tracing.
533 
534 void
zchunk_fprint(zchunk_t * self,FILE * file)535 zchunk_fprint (zchunk_t *self, FILE *file)
536 {
537     assert (self);
538     assert (zchunk_is (self));
539 
540     fprintf (file, "--------------------------------------\n");
541     if (!self) {
542         fprintf (file, "NULL");
543         return;
544     }
545     assert (self);
546     int is_bin = 0;
547     uint char_nbr;
548     for (char_nbr = 0; char_nbr < self->size; char_nbr++)
549         if (self->data [char_nbr] < 9 || self->data [char_nbr] > 127)
550             is_bin = 1;
551 
552     fprintf (file, "[%03d] ", (int) self->size);
553     for (char_nbr = 0; char_nbr < self->size; char_nbr++) {
554         if (is_bin) {
555             fprintf (file, "%02X", (unsigned char) self->data [char_nbr]);
556             if (char_nbr > 35) {
557                 fprintf (file, "...");
558                 break;
559             }
560         }
561         else {
562             fprintf (file, "%c", self->data [char_nbr]);
563             if (char_nbr > 70) {
564                 fprintf (file, "...");
565                 break;
566             }
567         }
568     }
569     fprintf (file, "\n");
570 }
571 
572 
573 
574 //  --------------------------------------------------------------------------
575 //  Dump message to stderr, for debugging and tracing.
576 //  See zchunk_fprint for details
577 
578 void
zchunk_print(zchunk_t * self)579 zchunk_print (zchunk_t *self)
580 {
581     assert (self);
582     assert (zchunk_is (self));
583 
584     zchunk_fprint (self, stderr);
585 }
586 
587 
588 //  --------------------------------------------------------------------------
589 //  Probe the supplied object, and report if it looks like a zchunk_t.
590 
591 bool
zchunk_is(void * self)592 zchunk_is (void *self)
593 {
594     assert (self);
595     return ((zchunk_t *) self)->tag == ZCHUNK_TAG;
596 }
597 
598 
599 //  --------------------------------------------------------------------------
600 //  Self test of this class
601 
602 
603 static void
mem_destructor(void ** hint)604 mem_destructor (void **hint) {
605     strcpy ((char*)*hint, "world");
606 }
607 
608 
609 void
zchunk_test(bool verbose)610 zchunk_test (bool verbose)
611 {
612     printf (" * zchunk: ");
613 
614     //  @selftest
615     zchunk_t *chunk = zchunk_new ("1234567890", 10);
616     assert (chunk);
617     assert (zchunk_size (chunk) == 10);
618     assert (memcmp (zchunk_data (chunk), "1234567890", 10) == 0);
619     zchunk_destroy (&chunk);
620 
621     chunk = zchunk_new (NULL, 10);
622     assert (chunk);
623     zchunk_append (chunk, "12345678", 8);
624     zchunk_append (chunk, "90ABCDEF", 8);
625     zchunk_append (chunk, "GHIJKLMN", 8);
626     assert (memcmp (zchunk_data (chunk), "1234567890", 10) == 0);
627     assert (zchunk_size (chunk) == 10);
628     assert (zchunk_streq (chunk, "1234567890"));
629     assert (streq (zchunk_digest (chunk), "01B307ACBA4F54F55AAFC33BB06BBBF6CA803E9A"));
630     char *string = zchunk_strdup (chunk);
631     assert (streq (string, "1234567890"));
632     freen (string);
633     string = zchunk_strhex (chunk);
634     assert (streq (string, "31323334353637383930"));
635     freen (string);
636 
637     zframe_t *frame = zchunk_pack (chunk);
638     assert (frame);
639 
640     zchunk_t *chunk2 = zchunk_unpack (frame);
641     assert (chunk2);
642     assert (memcmp (zchunk_data (chunk2), "1234567890", 10) == 0);
643     zframe_destroy (&frame);
644     zchunk_destroy (&chunk2);
645 
646     zchunk_t *copy = zchunk_dup (chunk);
647     assert (copy);
648     assert (memcmp (zchunk_data (copy), "1234567890", 10) == 0);
649     assert (zchunk_size (copy) == 10);
650     zchunk_destroy (&copy);
651     zchunk_destroy (&chunk);
652 
653     chunk = zchunk_new (NULL, 0);
654     zchunk_extend (chunk, "12345678", 8);
655     zchunk_extend (chunk, "90ABCDEF", 8);
656     zchunk_extend (chunk, "GHIJKLMN", 8);
657     assert (zchunk_size (chunk) == 24);
658     assert (zchunk_streq (chunk, "1234567890ABCDEFGHIJKLMN"));
659     zchunk_destroy (&chunk);
660 
661     copy = zchunk_new ("1234567890abcdefghij", 20);
662     assert (copy);
663     chunk = zchunk_new (NULL, 8);
664     assert (chunk);
665     zchunk_consume (chunk, copy);
666     assert (!zchunk_exhausted (copy));
667     assert (memcmp (zchunk_data (chunk), "12345678", 8) == 0);
668     zchunk_set (chunk, NULL, 0);
669     zchunk_consume (chunk, copy);
670     assert (!zchunk_exhausted (copy));
671     assert (memcmp (zchunk_data (chunk), "90abcdef", 8) == 0);
672     zchunk_set (chunk, NULL, 0);
673     zchunk_consume (chunk, copy);
674     assert (zchunk_exhausted (copy));
675     assert (zchunk_size (chunk) == 4);
676     assert (memcmp (zchunk_data (chunk), "ghij", 4) == 0);
677     zchunk_destroy (&copy);
678     zchunk_destroy (&chunk);
679 
680     char str[] = "hello";
681     chunk = zchunk_frommem (str, 5, mem_destructor, str);
682     assert (chunk);
683     zchunk_destroy (&chunk);
684 
685     //  The destructor doesn't free the memory, only changing the strid,
686     //  so we can check if the destructor was invoked
687     assert (streq (str, "world"));
688 
689     chunk = zchunk_new ("1234567890", 10);
690     frame = zchunk_packx (&chunk);
691     assert (frame);
692     assert (chunk == NULL);
693 
694     chunk = zchunk_unpack (frame);
695     assert (chunk);
696     assert (memcmp (zchunk_data (chunk), "1234567890", 10) == 0);
697     zframe_destroy (&frame);
698     zchunk_destroy (&chunk);
699 
700 #if defined (__WINDOWS__)
701     zsys_shutdown();
702 #endif
703     //  @end
704 
705     printf ("OK\n");
706 }
707