Rewrite header caching
Showing
1 changed file
with
94 additions
and
149 deletions
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | 1 | /* GNU Mailutils -- a suite of utilities for electronic mail |
2 | Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. | 2 | Copyright (C) 1999, 2000, 2001, 2004, 2005, |
3 | 2007 Free Software Foundation, Inc. | ||
3 | 4 | ||
4 | This library is free software; you can redistribute it and/or | 5 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public | 6 | modify it under the terms of the GNU Lesser General Public |
... | @@ -66,21 +67,7 @@ mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner) | ... | @@ -66,21 +67,7 @@ mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner) |
66 | static void | 67 | static void |
67 | header_free_cache (mu_header_t header) | 68 | header_free_cache (mu_header_t header) |
68 | { | 69 | { |
69 | /* Clean up our fast header cache. */ | 70 | mu_assoc_clear (header->cache); |
70 | if (header->fhdr) | ||
71 | { | ||
72 | size_t i; | ||
73 | for (i = 0; i < header->fhdr_count; i++) | ||
74 | { | ||
75 | if (header->fhdr[i].fn) | ||
76 | free (header->fhdr[i].fn); | ||
77 | if (header->fhdr[i].fv) | ||
78 | free (header->fhdr[i].fv); | ||
79 | } | ||
80 | free (header->fhdr); | ||
81 | header->fhdr = NULL; | ||
82 | header->fhdr_count = 0; | ||
83 | } | ||
84 | } | 71 | } |
85 | 72 | ||
86 | void | 73 | void |
... | @@ -103,6 +90,8 @@ mu_header_destroy (mu_header_t *ph, void *owner) | ... | @@ -103,6 +90,8 @@ mu_header_destroy (mu_header_t *ph, void *owner) |
103 | 90 | ||
104 | header_free_cache (header); | 91 | header_free_cache (header); |
105 | 92 | ||
93 | mu_assoc_destroy (&header->cache); | ||
94 | |||
106 | if (header->mstream) | 95 | if (header->mstream) |
107 | mu_stream_destroy (&(header->mstream), NULL); | 96 | mu_stream_destroy (&(header->mstream), NULL); |
108 | 97 | ||
... | @@ -145,10 +134,12 @@ header_parse (mu_header_t header, const char *blurb, int len) | ... | @@ -145,10 +134,12 @@ header_parse (mu_header_t header, const char *blurb, int len) |
145 | char *header_start2; | 134 | char *header_start2; |
146 | struct _hdr *hdr; | 135 | struct _hdr *hdr; |
147 | 136 | ||
137 | header_free_cache (header); | ||
138 | |||
148 | /* Nothing to parse. */ | 139 | /* Nothing to parse. */ |
149 | if (blurb == NULL) | 140 | if (blurb == NULL) |
150 | return 0; | 141 | return 0; |
151 | 142 | ||
152 | header->blurb_len = len; | 143 | header->blurb_len = len; |
153 | /* Why "+ 1", if for a terminating NULL, where is written? */ | 144 | /* Why "+ 1", if for a terminating NULL, where is written? */ |
154 | header->blurb = calloc (1, header->blurb_len + 1); | 145 | header->blurb = calloc (1, header->blurb_len + 1); |
... | @@ -257,8 +248,8 @@ header_parse (mu_header_t header, const char *blurb, int len) | ... | @@ -257,8 +248,8 @@ header_parse (mu_header_t header, const char *blurb, int len) |
257 | return 0; | 248 | return 0; |
258 | } | 249 | } |
259 | 250 | ||
260 | /* FIXME: grossly inneficient, to many copies and reallocating. | 251 | /* FIXME: grossly inneficient, too many copies and reallocating. |
261 | This all header business need a good rewrite. */ | 252 | This all header business needs a good rewrite. */ |
262 | int | 253 | int |
263 | mu_header_set_value (mu_header_t header, const char *fn, const char *fv, | 254 | mu_header_set_value (mu_header_t header, const char *fn, const char *fv, |
264 | int replace) | 255 | int replace) |
... | @@ -395,100 +386,94 @@ mu_header_set_value (mu_header_t header, const char *fn, const char *fv, | ... | @@ -395,100 +386,94 @@ mu_header_set_value (mu_header_t header, const char *fn, const char *fv, |
395 | return 0; | 386 | return 0; |
396 | } | 387 | } |
397 | 388 | ||
398 | /* We try to cache the headers here to reduce networking access | 389 | struct _hdr_cache { |
399 | especially for IMAP. When the buffer is NULL it means that | 390 | size_t len; |
400 | the field does not exist on the server and we should not | 391 | char *ptr; |
401 | attempt to contact the server again for this field. */ | 392 | }; |
402 | static int | ||
403 | header_set_fvalue (mu_header_t header, const char *name, char *buffer) | ||
404 | { | ||
405 | struct _hdr *thdr; | ||
406 | thdr = realloc (header->fhdr, (header->fhdr_count + 1) * sizeof(*thdr)); | ||
407 | if (thdr) | ||
408 | { | ||
409 | size_t len = strlen (name); | ||
410 | char *field = malloc (len + 1); | ||
411 | if (field == NULL) | ||
412 | return ENOMEM; | ||
413 | memcpy (field, name, len); | ||
414 | field[len] = '\0'; | ||
415 | thdr[header->fhdr_count].fn = field; | ||
416 | thdr[header->fhdr_count].fn_end = field + len; | ||
417 | 393 | ||
418 | if (buffer) | 394 | static void |
419 | { | 395 | _hdr_cache_destroy (void *data) |
420 | len = strlen (buffer); | 396 | { |
421 | field = malloc (len + 1); | 397 | struct _hdr_cache *hc = data; |
422 | if (field == NULL) | 398 | free (hc->ptr); |
423 | return ENOMEM; | ||
424 | memcpy (field, buffer, len); | ||
425 | field[len] = '\0'; | ||
426 | thdr[header->fhdr_count].fv = field; | ||
427 | thdr[header->fhdr_count].fv_end = field + len; | ||
428 | } | ||
429 | else | ||
430 | { | ||
431 | thdr[header->fhdr_count].fv = NULL; | ||
432 | thdr[header->fhdr_count].fv_end = NULL; | ||
433 | } | ||
434 | header->fhdr_count++; | ||
435 | header->fhdr = thdr; | ||
436 | return 0; | ||
437 | } | ||
438 | return ENOMEM; | ||
439 | } | 399 | } |
440 | 400 | ||
441 | /* For the cache header if the field exist but with no corresponding | ||
442 | value, it is a permanent failure i.e. the field does not exist | ||
443 | in the header return EINVAL to notify mu_header_get_value(). */ | ||
444 | static int | 401 | static int |
445 | header_get_fvalue (mu_header_t header, const char *name, char *buffer, | 402 | header_get_fvalue (mu_header_t header, const char *name, char *buffer, |
446 | size_t buflen, size_t *pn) | 403 | size_t buflen, size_t *pn) |
447 | { | 404 | { |
448 | size_t i, fn_len, fv_len = 0; | 405 | struct _hdr_cache *hdr = mu_assoc_ref (header->cache, name); |
449 | size_t name_len; | 406 | if (hdr) |
450 | int err = MU_ERR_NOENT; | ||
451 | |||
452 | for (i = 0, name_len = strlen (name); i < header->fhdr_count; i++) | ||
453 | { | 407 | { |
454 | fn_len = header->fhdr[i].fn_end - header->fhdr[i].fn; | 408 | if (hdr->len == 0) |
455 | if (fn_len == name_len | 409 | return MU_ERR_NOENT; |
456 | && strcasecmp (header->fhdr[i].fn, name) == 0) | 410 | else |
457 | { | 411 | { |
458 | fv_len = header->fhdr[i].fv_end - header->fhdr[i].fv; | 412 | size_t fv_len = hdr->len; |
459 | |||
460 | /* Permanent failure. */ | ||
461 | if (fv_len == 0) | ||
462 | { | ||
463 | err = EINVAL; | ||
464 | break; | ||
465 | } | ||
466 | 413 | ||
467 | if (buffer && buflen > 0) | 414 | if (buffer && buflen > 0) |
468 | { | 415 | { |
469 | buflen--; | 416 | buflen--; |
470 | fv_len = (fv_len < buflen) ? fv_len : buflen; | 417 | fv_len = (fv_len < buflen) ? fv_len : buflen; |
471 | memcpy (buffer, header->fhdr[i].fv, fv_len); | 418 | memcpy (buffer, hdr->ptr, fv_len); |
472 | buffer[fv_len] = '\0'; | 419 | buffer[fv_len] = '\0'; |
473 | } | 420 | } |
474 | err = 0; | 421 | if (pn) |
475 | break; | 422 | *pn = fv_len; |
423 | return 0; | ||
476 | } | 424 | } |
477 | } | 425 | } |
478 | if (pn) | 426 | return MU_ERR_NOENT; |
479 | *pn = fv_len; | ||
480 | return err; | ||
481 | |||
482 | } | 427 | } |
483 | 428 | ||
429 | /* We try to cache the headers here to reduce networking access | ||
430 | especially for IMAP. When the buffer is NULL it means that | ||
431 | the field does not exist on the server and we should not | ||
432 | attempt to contact the server again for this field. */ | ||
433 | static int | ||
434 | header_set_fvalue (mu_header_t header, const char *name, char *buffer, | ||
435 | size_t len) | ||
436 | { | ||
437 | int rc; | ||
438 | struct _hdr_cache hcache; | ||
439 | |||
440 | if (!header->cache) | ||
441 | { | ||
442 | rc = mu_assoc_create (&header->cache, sizeof(struct _hdr_cache), | ||
443 | MU_ASSOC_ICASE); | ||
444 | if (rc) | ||
445 | return rc; | ||
446 | mu_assoc_set_free (header->cache, _hdr_cache_destroy); | ||
447 | } | ||
448 | if (buffer == NULL) | ||
449 | { | ||
450 | hcache.ptr = NULL; | ||
451 | hcache.len = 0; | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | if (!len) | ||
456 | len = strlen (buffer); | ||
457 | hcache.ptr = malloc (len + 1); | ||
458 | if (!hcache.ptr) | ||
459 | return ENOMEM; | ||
460 | memcpy (hcache.ptr, buffer, len); | ||
461 | hcache.ptr[len] = 0; | ||
462 | hcache.len = len; | ||
463 | } | ||
464 | rc = mu_assoc_install (header->cache, name, &hcache); | ||
465 | if (rc) | ||
466 | free (hcache.ptr); | ||
467 | return rc; | ||
468 | } | ||
469 | |||
484 | int | 470 | int |
485 | mu_header_get_value (mu_header_t header, const char *name, char *buffer, | 471 | mu_header_get_value (mu_header_t header, const char *name, char *buffer, |
486 | size_t buflen, size_t *pn) | 472 | size_t buflen, size_t *pn) |
487 | { | 473 | { |
488 | size_t i = 0; | 474 | size_t i = 0; |
489 | size_t name_len; | 475 | size_t name_len; |
490 | size_t total = 0, fn_len = 0, fv_len = 0; | 476 | size_t fn_len = 0, fv_len = 0; |
491 | size_t threshold; | ||
492 | int err = 0; | 477 | int err = 0; |
493 | 478 | ||
494 | if (header == NULL || name == NULL) | 479 | if (header == NULL || name == NULL) |
... | @@ -507,12 +492,6 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, | ... | @@ -507,12 +492,6 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, |
507 | return err; | 492 | return err; |
508 | } | 493 | } |
509 | 494 | ||
510 | /* Try the provided cache. */ | ||
511 | if (header->_get_fvalue) | ||
512 | err = header->_get_fvalue (header, name, buffer, buflen, pn); | ||
513 | if (err == 0) | ||
514 | return 0; | ||
515 | |||
516 | if (header->_get_value) | 495 | if (header->_get_value) |
517 | { | 496 | { |
518 | char buf[1024]; /* should suffice for field-value. */ | 497 | char buf[1024]; /* should suffice for field-value. */ |
... | @@ -521,7 +500,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, | ... | @@ -521,7 +500,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, |
521 | if (err == 0) | 500 | if (err == 0) |
522 | { | 501 | { |
523 | /* Save in the fast header buffer. */ | 502 | /* Save in the fast header buffer. */ |
524 | header_set_fvalue (header, name, buf); | 503 | header_set_fvalue (header, name, buf, 0); |
525 | if (buffer && buflen > 0) | 504 | if (buffer && buflen > 0) |
526 | { | 505 | { |
527 | buflen--; | 506 | buflen--; |
... | @@ -537,7 +516,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, | ... | @@ -537,7 +516,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, |
537 | else | 516 | else |
538 | { | 517 | { |
539 | /* Cache permanent failure also. */ | 518 | /* Cache permanent failure also. */ |
540 | header_set_fvalue (header, name, NULL); | 519 | header_set_fvalue (header, name, NULL, 0); |
541 | } | 520 | } |
542 | return err; | 521 | return err; |
543 | } | 522 | } |
... | @@ -551,15 +530,8 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, | ... | @@ -551,15 +530,8 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, |
551 | } | 530 | } |
552 | 531 | ||
553 | /* We set the threshold to be 1 less for the null. */ | 532 | /* We set the threshold to be 1 less for the null. */ |
554 | threshold = --buflen; | 533 | --buflen; |
555 | 534 | ||
556 | /* Caution: We may have more then one value for a field name, for example | ||
557 | a "Received" field-name is added by each passing MTA. The way that the | ||
558 | parsing (_parse()) is done it's not take to account. So we just stuff | ||
559 | in the buffer all the field-values to a corresponding field-name. | ||
560 | FIXME: Should we kosher the output ? meaning replace occurences of | ||
561 | " \t\r\n" for spaces ? for now we don't. | ||
562 | */ | ||
563 | for (name_len = strlen (name), i = 0; i < header->hdr_count; i++) | 535 | for (name_len = strlen (name), i = 0; i < header->hdr_count; i++) |
564 | { | 536 | { |
565 | fn_len = header->hdr[i].fn_end - header->hdr[i].fn; | 537 | fn_len = header->hdr[i].fn_end - header->hdr[i].fn; |
... | @@ -567,39 +539,26 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, | ... | @@ -567,39 +539,26 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, |
567 | strncasecmp (header->hdr[i].fn, name, fn_len) == 0) | 539 | strncasecmp (header->hdr[i].fn, name, fn_len) == 0) |
568 | { | 540 | { |
569 | fv_len = (header->hdr[i].fv_end - header->hdr[i].fv); | 541 | fv_len = (header->hdr[i].fv_end - header->hdr[i].fv); |
570 | /* FIXME:FIXME:PLEASE: hack, add a space/nl separator */ | 542 | header_set_fvalue (header, name, header->hdr[i].fv, fv_len); |
571 | /* | ||
572 | if (total && (threshold - 2) > 0) | ||
573 | { | ||
574 | if (buffer) | ||
575 | { | ||
576 | *buffer++ = '\n'; | ||
577 | *buffer++ = ' '; | ||
578 | } | ||
579 | threshold -= 2; | ||
580 | total += 2; | ||
581 | } | ||
582 | */ | ||
583 | total += fv_len; | ||
584 | /* Can everything fit in the buffer. */ | 543 | /* Can everything fit in the buffer. */ |
585 | if (buffer && threshold > 0) | 544 | if (buffer && buflen > 0) |
586 | { | 545 | { |
587 | buflen = (fv_len < threshold) ? fv_len : threshold; | 546 | if (fv_len > buflen) |
588 | memcpy (buffer, header->hdr[i].fv, buflen); | 547 | fv_len = buflen; |
589 | buffer += buflen; | 548 | memcpy (buffer, header->hdr[i].fv, fv_len); |
590 | threshold -= buflen; | 549 | buffer[buflen] = 0; |
591 | } | 550 | } |
592 | 551 | ||
593 | /* Jump out after the first header we found. -sr */ | 552 | if (pn) |
594 | break; | 553 | *pn = fv_len; |
554 | return 0; | ||
595 | } | 555 | } |
596 | } | 556 | } |
597 | if (buffer) | ||
598 | *buffer = '\0'; /* Null terminated. */ | ||
599 | if (pn) | ||
600 | *pn = total; | ||
601 | 557 | ||
602 | return (total == 0) ? MU_ERR_NOENT : 0; | 558 | if (buffer) |
559 | *buffer = 0; | ||
560 | |||
561 | return MU_ERR_NOENT; | ||
603 | } | 562 | } |
604 | 563 | ||
605 | int | 564 | int |
... | @@ -888,19 +847,6 @@ mu_header_size (mu_header_t header, size_t *psize) | ... | @@ -888,19 +847,6 @@ mu_header_size (mu_header_t header, size_t *psize) |
888 | } | 847 | } |
889 | 848 | ||
890 | int | 849 | int |
891 | mu_header_set_get_fvalue (mu_header_t header, | ||
892 | int (*_get_fvalue) (mu_header_t, const char *, char *, size_t, size_t *), | ||
893 | void *owner) | ||
894 | { | ||
895 | if (header == NULL) | ||
896 | return EINVAL; | ||
897 | if (header->owner != owner) | ||
898 | return EACCES; | ||
899 | header->_get_fvalue = _get_fvalue; | ||
900 | return 0; | ||
901 | } | ||
902 | |||
903 | int | ||
904 | mu_header_set_get_value (mu_header_t header, int (*_get_value) | 850 | mu_header_set_get_value (mu_header_t header, int (*_get_value) |
905 | (mu_header_t, const char *, char *, size_t, size_t *), | 851 | (mu_header_t, const char *, char *, size_t, size_t *), |
906 | void *owner) | 852 | void *owner) |
... | @@ -939,8 +885,8 @@ mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner) | ... | @@ -939,8 +885,8 @@ mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner) |
939 | 885 | ||
940 | int | 886 | int |
941 | mu_header_set_fill (mu_header_t header, int | 887 | mu_header_set_fill (mu_header_t header, int |
942 | (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *), | 888 | (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *), |
943 | void *owner) | 889 | void *owner) |
944 | { | 890 | { |
945 | if (header == NULL) | 891 | if (header == NULL) |
946 | return EINVAL; | 892 | return EINVAL; |
... | @@ -963,7 +909,6 @@ fill_blurb (mu_header_t header) | ... | @@ -963,7 +909,6 @@ fill_blurb (mu_header_t header) |
963 | /* The entire header is now ours(part of mu_header_t), clear all the | 909 | /* The entire header is now ours(part of mu_header_t), clear all the |
964 | overloading. */ | 910 | overloading. */ |
965 | header_free_cache (header); | 911 | header_free_cache (header); |
966 | header->_get_fvalue = NULL; | ||
967 | header->_get_value = NULL; | 912 | header->_get_value = NULL; |
968 | header->_set_value = NULL; | 913 | header->_set_value = NULL; |
969 | header->_size = NULL; | 914 | header->_size = NULL; | ... | ... |
-
Please register or sign in to post a comment