Fossil SCM
Join duplicated footnotes slightly faster. Fix a comment about auxiliary <tt>cmp_footnote_id()</tt> function.
Commit
7f6a641808be00f8e68b5c23ce1ec8f3a1964ba53da68ed6076aa8d32fce6471
Parent
92221aaa192e82e…
1 file changed
+29
-36
+29
-36
| --- src/markdown.c | ||
| +++ src/markdown.c | ||
| @@ -274,11 +274,11 @@ | ||
| 274 | 274 | return blob_compare(&lra->id, &lrb->id); |
| 275 | 275 | } |
| 276 | 276 | |
| 277 | 277 | /* cmp_footnote_id -- comparison function for footnotes qsort. |
| 278 | 278 | * Empty IDs sort last (in undetermined order). |
| 279 | - * Equal IDs are sorted in the REVERSED order of definition in the source */ | |
| 279 | + * Equal IDs are sorted in the order of definition in the source */ | |
| 280 | 280 | static int cmp_footnote_id(const void *fna, const void *fnb){ |
| 281 | 281 | const struct footnote *a = fna, *b = fnb; |
| 282 | 282 | const int szA = blob_size(&a->id), szB = blob_size(&b->id); |
| 283 | 283 | if( szA ){ |
| 284 | 284 | if( szB ){ |
| @@ -2498,10 +2498,11 @@ | ||
| 2498 | 2498 | struct link_ref *lr; |
| 2499 | 2499 | struct footnote *fn; |
| 2500 | 2500 | size_t i, beg, end = 0; |
| 2501 | 2501 | struct render rndr; |
| 2502 | 2502 | Blob text = BLOB_INITIALIZER; /* input after the first pass */ |
| 2503 | + Blob *allNotes = &rndr.notes.all; | |
| 2503 | 2504 | |
| 2504 | 2505 | /* filling the render structure */ |
| 2505 | 2506 | if( !rndrer ) return; |
| 2506 | 2507 | rndr.make = *rndrer; |
| 2507 | 2508 | rndr.nBlobCache = 0; |
| @@ -2565,24 +2566,26 @@ | ||
| 2565 | 2566 | qsort(blob_buffer(&rndr.refs), |
| 2566 | 2567 | blob_size(&rndr.refs)/sizeof(struct link_ref), |
| 2567 | 2568 | sizeof(struct link_ref), |
| 2568 | 2569 | cmp_link_ref_sort); |
| 2569 | 2570 | } |
| 2570 | - rndr.notes.nLbled = COUNT_FOOTNOTES(&rndr.notes.all); | |
| 2571 | + rndr.notes.nLbled = COUNT_FOOTNOTES( allNotes ); | |
| 2571 | 2572 | |
| 2572 | 2573 | /* sort footnotes by ID and join duplicates */ |
| 2573 | 2574 | if( rndr.notes.nLbled > 1 ){ |
| 2574 | - fn = CAST_AS_FOOTNOTES(&rndr.notes.all); | |
| 2575 | + int nDups = 0; | |
| 2576 | + fn = CAST_AS_FOOTNOTES( allNotes ); | |
| 2575 | 2577 | qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); |
| 2576 | 2578 | |
| 2577 | 2579 | /* concatenate footnotes with equal labels */ |
| 2578 | 2580 | for(i=0; i<rndr.notes.nLbled ;){ |
| 2579 | 2581 | struct footnote *x = fn + i; |
| 2580 | 2582 | size_t j = i+1, k = blob_size(&x->text) + 64; |
| 2581 | 2583 | while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){ |
| 2582 | 2584 | k += blob_size(&fn[j].text) + 10; |
| 2583 | 2585 | j++; |
| 2586 | + nDups++; | |
| 2584 | 2587 | } |
| 2585 | 2588 | if( i+1<j ){ |
| 2586 | 2589 | Blob tmp = empty_blob; |
| 2587 | 2590 | blob_reserve(&tmp, k); |
| 2588 | 2591 | blob_append_string(&tmp, "<ul class='footnote-joined'>\n"); |
| @@ -2592,76 +2595,66 @@ | ||
| 2592 | 2595 | blob_append(&tmp, blob_buffer(&y->text), blob_size(&y->text)); |
| 2593 | 2596 | blob_append_string(&tmp, "</li>\n"); |
| 2594 | 2597 | |
| 2595 | 2598 | /* free memory buffer */ |
| 2596 | 2599 | blob_reset(&y->text); |
| 2597 | - if( k!=i ){ | |
| 2598 | - blob_reset(&y->id); | |
| 2599 | - /* invalidate redundant elements (this is optional) */ | |
| 2600 | - memset(y,0,sizeof(struct footnote)); | |
| 2601 | - y->index = y->defno = y->iMark = y->nUsed = -42; | |
| 2602 | - } | |
| 2600 | + if( k!=i ) blob_reset(&y->id); | |
| 2603 | 2601 | } |
| 2604 | 2602 | blob_append_string(&tmp, "</ul>\n"); |
| 2605 | 2603 | x->text = tmp; |
| 2606 | 2604 | } |
| 2607 | 2605 | i = j; |
| 2608 | 2606 | } |
| 2609 | - | |
| 2610 | - /* move redundant elements to the end of array and truncate/resize */ | |
| 2611 | - qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); | |
| 2612 | - i = rndr.notes.nLbled; | |
| 2613 | - while( i && !blob_size(&fn[i-1].id) ){ i--; } | |
| 2614 | - rndr.notes.nLbled = i; | |
| 2615 | - blob_resize( &rndr.notes.all, i*sizeof(struct footnote) ); | |
| 2616 | - | |
| 2617 | - /* FIXME: It was expected to work via truncation: | |
| 2618 | - * | |
| 2619 | - * blob_truncate( &rndr.notes.all, i*sizeof(struct footnote) ); | |
| 2620 | - * | |
| 2621 | - * but that way it crashes with | |
| 2622 | - * | |
| 2623 | - * free(): double free detected in tcache 2 | |
| 2624 | - * | |
| 2625 | - * This is strange. */ | |
| 2626 | - } | |
| 2627 | - assert( COUNT_FOOTNOTES(&rndr.notes.all) == rndr.notes.nLbled ); | |
| 2628 | - fn = CAST_AS_FOOTNOTES(&rndr.notes.all); | |
| 2607 | + if( nDups ){ /* clean rndr.notes.all from invalidated footnotes */ | |
| 2608 | + const int n = rndr.notes.nLbled - nDups; | |
| 2609 | + struct Blob filtered = empty_blob; | |
| 2610 | + blob_reserve(&filtered, n*sizeof(struct footnote)); | |
| 2611 | + for(i=0; i<rndr.notes.nLbled; i++){ | |
| 2612 | + if( blob_size(&fn[i].id) ){ | |
| 2613 | + blob_append(&filtered, (char*)(fn+i), sizeof(struct footnote)); | |
| 2614 | + } | |
| 2615 | + } | |
| 2616 | + blob_reset( allNotes ); | |
| 2617 | + rndr.notes.all = filtered; | |
| 2618 | + rndr.notes.nLbled = n; | |
| 2619 | + assert( COUNT_FOOTNOTES(allNotes) == rndr.notes.nLbled ); | |
| 2620 | + } | |
| 2621 | + } | |
| 2622 | + fn = CAST_AS_FOOTNOTES( allNotes ); | |
| 2629 | 2623 | for(i=0; i<rndr.notes.nLbled; i++){ |
| 2630 | 2624 | fn[i].index = i; |
| 2631 | 2625 | } |
| 2632 | 2626 | assert( rndr.notes.nMarks==0 ); |
| 2633 | 2627 | |
| 2634 | 2628 | /* second pass: actual rendering */ |
| 2635 | 2629 | if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); |
| 2636 | 2630 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); |
| 2637 | 2631 | |
| 2638 | - if( (blob_size(&rndr.notes.all) || rndr.notes.misref.nUsed) ){ | |
| 2632 | + if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ | |
| 2639 | 2633 | |
| 2640 | 2634 | /* Footnotes must be parsed for the correct discovery of (back)links */ |
| 2641 | 2635 | Blob *notes = new_work_buffer( &rndr ); |
| 2642 | 2636 | Blob *tmp = new_work_buffer( &rndr ); |
| 2643 | - const struct Blob *origin = &rndr.notes.all; | |
| 2644 | 2637 | int nMarks = -1; |
| 2645 | 2638 | |
| 2646 | 2639 | /* inline notes may get appended to rndr.notes.all while rendering */ |
| 2647 | 2640 | while(1){ |
| 2648 | 2641 | struct footnote *aNotes; |
| 2649 | - const int N = COUNT_FOOTNOTES(origin); | |
| 2642 | + const int N = COUNT_FOOTNOTES( allNotes ); | |
| 2650 | 2643 | |
| 2651 | 2644 | /* make a shallow copy of `origin` */ |
| 2652 | 2645 | blob_truncate(notes,0); |
| 2653 | - blob_append(notes, blob_buffer(origin), blob_size(origin)); | |
| 2646 | + blob_append(notes, blob_buffer(allNotes), blob_size(allNotes)); | |
| 2654 | 2647 | aNotes = CAST_AS_FOOTNOTES(notes); |
| 2655 | 2648 | qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); |
| 2656 | 2649 | |
| 2657 | 2650 | if( nMarks == rndr.notes.nMarks ) break; |
| 2658 | 2651 | nMarks = rndr.notes.nMarks; |
| 2659 | 2652 | |
| 2660 | 2653 | for(i=0; i<N; i++){ |
| 2661 | 2654 | const int j = aNotes[i].index; |
| 2662 | - struct footnote *x = CAST_AS_FOOTNOTES(origin) + j; | |
| 2655 | + struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; | |
| 2663 | 2656 | assert( 0<=j && j<N ); |
| 2664 | 2657 | if( x->bRndred || !x->nUsed ) continue; |
| 2665 | 2658 | assert( x->iMark > 0 ); |
| 2666 | 2659 | assert( blob_size(&x->text) ); |
| 2667 | 2660 | blob_truncate(tmp,0); |
| @@ -2708,16 +2701,16 @@ | ||
| 2708 | 2701 | blob_reset(&lr[i].id); |
| 2709 | 2702 | blob_reset(&lr[i].link); |
| 2710 | 2703 | blob_reset(&lr[i].title); |
| 2711 | 2704 | } |
| 2712 | 2705 | blob_reset(&rndr.refs); |
| 2713 | - fn = CAST_AS_FOOTNOTES(&rndr.notes.all); | |
| 2714 | - end = COUNT_FOOTNOTES(&rndr.notes.all); | |
| 2706 | + fn = CAST_AS_FOOTNOTES( allNotes ); | |
| 2707 | + end = COUNT_FOOTNOTES( allNotes ); | |
| 2715 | 2708 | for(i=0; i<end; i++){ |
| 2716 | 2709 | if(blob_size(&fn[i].id)) blob_reset(&fn[i].id); |
| 2717 | 2710 | blob_reset(&fn[i].text); |
| 2718 | 2711 | } |
| 2719 | 2712 | blob_reset(&rndr.notes.all); |
| 2720 | 2713 | for(i=0; i<rndr.nBlobCache; i++){ |
| 2721 | 2714 | fossil_free(rndr.aBlobCache[i]); |
| 2722 | 2715 | } |
| 2723 | 2716 | } |
| 2724 | 2717 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -274,11 +274,11 @@ | |
| 274 | return blob_compare(&lra->id, &lrb->id); |
| 275 | } |
| 276 | |
| 277 | /* cmp_footnote_id -- comparison function for footnotes qsort. |
| 278 | * Empty IDs sort last (in undetermined order). |
| 279 | * Equal IDs are sorted in the REVERSED order of definition in the source */ |
| 280 | static int cmp_footnote_id(const void *fna, const void *fnb){ |
| 281 | const struct footnote *a = fna, *b = fnb; |
| 282 | const int szA = blob_size(&a->id), szB = blob_size(&b->id); |
| 283 | if( szA ){ |
| 284 | if( szB ){ |
| @@ -2498,10 +2498,11 @@ | |
| 2498 | struct link_ref *lr; |
| 2499 | struct footnote *fn; |
| 2500 | size_t i, beg, end = 0; |
| 2501 | struct render rndr; |
| 2502 | Blob text = BLOB_INITIALIZER; /* input after the first pass */ |
| 2503 | |
| 2504 | /* filling the render structure */ |
| 2505 | if( !rndrer ) return; |
| 2506 | rndr.make = *rndrer; |
| 2507 | rndr.nBlobCache = 0; |
| @@ -2565,24 +2566,26 @@ | |
| 2565 | qsort(blob_buffer(&rndr.refs), |
| 2566 | blob_size(&rndr.refs)/sizeof(struct link_ref), |
| 2567 | sizeof(struct link_ref), |
| 2568 | cmp_link_ref_sort); |
| 2569 | } |
| 2570 | rndr.notes.nLbled = COUNT_FOOTNOTES(&rndr.notes.all); |
| 2571 | |
| 2572 | /* sort footnotes by ID and join duplicates */ |
| 2573 | if( rndr.notes.nLbled > 1 ){ |
| 2574 | fn = CAST_AS_FOOTNOTES(&rndr.notes.all); |
| 2575 | qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); |
| 2576 | |
| 2577 | /* concatenate footnotes with equal labels */ |
| 2578 | for(i=0; i<rndr.notes.nLbled ;){ |
| 2579 | struct footnote *x = fn + i; |
| 2580 | size_t j = i+1, k = blob_size(&x->text) + 64; |
| 2581 | while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){ |
| 2582 | k += blob_size(&fn[j].text) + 10; |
| 2583 | j++; |
| 2584 | } |
| 2585 | if( i+1<j ){ |
| 2586 | Blob tmp = empty_blob; |
| 2587 | blob_reserve(&tmp, k); |
| 2588 | blob_append_string(&tmp, "<ul class='footnote-joined'>\n"); |
| @@ -2592,76 +2595,66 @@ | |
| 2592 | blob_append(&tmp, blob_buffer(&y->text), blob_size(&y->text)); |
| 2593 | blob_append_string(&tmp, "</li>\n"); |
| 2594 | |
| 2595 | /* free memory buffer */ |
| 2596 | blob_reset(&y->text); |
| 2597 | if( k!=i ){ |
| 2598 | blob_reset(&y->id); |
| 2599 | /* invalidate redundant elements (this is optional) */ |
| 2600 | memset(y,0,sizeof(struct footnote)); |
| 2601 | y->index = y->defno = y->iMark = y->nUsed = -42; |
| 2602 | } |
| 2603 | } |
| 2604 | blob_append_string(&tmp, "</ul>\n"); |
| 2605 | x->text = tmp; |
| 2606 | } |
| 2607 | i = j; |
| 2608 | } |
| 2609 | |
| 2610 | /* move redundant elements to the end of array and truncate/resize */ |
| 2611 | qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); |
| 2612 | i = rndr.notes.nLbled; |
| 2613 | while( i && !blob_size(&fn[i-1].id) ){ i--; } |
| 2614 | rndr.notes.nLbled = i; |
| 2615 | blob_resize( &rndr.notes.all, i*sizeof(struct footnote) ); |
| 2616 | |
| 2617 | /* FIXME: It was expected to work via truncation: |
| 2618 | * |
| 2619 | * blob_truncate( &rndr.notes.all, i*sizeof(struct footnote) ); |
| 2620 | * |
| 2621 | * but that way it crashes with |
| 2622 | * |
| 2623 | * free(): double free detected in tcache 2 |
| 2624 | * |
| 2625 | * This is strange. */ |
| 2626 | } |
| 2627 | assert( COUNT_FOOTNOTES(&rndr.notes.all) == rndr.notes.nLbled ); |
| 2628 | fn = CAST_AS_FOOTNOTES(&rndr.notes.all); |
| 2629 | for(i=0; i<rndr.notes.nLbled; i++){ |
| 2630 | fn[i].index = i; |
| 2631 | } |
| 2632 | assert( rndr.notes.nMarks==0 ); |
| 2633 | |
| 2634 | /* second pass: actual rendering */ |
| 2635 | if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); |
| 2636 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); |
| 2637 | |
| 2638 | if( (blob_size(&rndr.notes.all) || rndr.notes.misref.nUsed) ){ |
| 2639 | |
| 2640 | /* Footnotes must be parsed for the correct discovery of (back)links */ |
| 2641 | Blob *notes = new_work_buffer( &rndr ); |
| 2642 | Blob *tmp = new_work_buffer( &rndr ); |
| 2643 | const struct Blob *origin = &rndr.notes.all; |
| 2644 | int nMarks = -1; |
| 2645 | |
| 2646 | /* inline notes may get appended to rndr.notes.all while rendering */ |
| 2647 | while(1){ |
| 2648 | struct footnote *aNotes; |
| 2649 | const int N = COUNT_FOOTNOTES(origin); |
| 2650 | |
| 2651 | /* make a shallow copy of `origin` */ |
| 2652 | blob_truncate(notes,0); |
| 2653 | blob_append(notes, blob_buffer(origin), blob_size(origin)); |
| 2654 | aNotes = CAST_AS_FOOTNOTES(notes); |
| 2655 | qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); |
| 2656 | |
| 2657 | if( nMarks == rndr.notes.nMarks ) break; |
| 2658 | nMarks = rndr.notes.nMarks; |
| 2659 | |
| 2660 | for(i=0; i<N; i++){ |
| 2661 | const int j = aNotes[i].index; |
| 2662 | struct footnote *x = CAST_AS_FOOTNOTES(origin) + j; |
| 2663 | assert( 0<=j && j<N ); |
| 2664 | if( x->bRndred || !x->nUsed ) continue; |
| 2665 | assert( x->iMark > 0 ); |
| 2666 | assert( blob_size(&x->text) ); |
| 2667 | blob_truncate(tmp,0); |
| @@ -2708,16 +2701,16 @@ | |
| 2708 | blob_reset(&lr[i].id); |
| 2709 | blob_reset(&lr[i].link); |
| 2710 | blob_reset(&lr[i].title); |
| 2711 | } |
| 2712 | blob_reset(&rndr.refs); |
| 2713 | fn = CAST_AS_FOOTNOTES(&rndr.notes.all); |
| 2714 | end = COUNT_FOOTNOTES(&rndr.notes.all); |
| 2715 | for(i=0; i<end; i++){ |
| 2716 | if(blob_size(&fn[i].id)) blob_reset(&fn[i].id); |
| 2717 | blob_reset(&fn[i].text); |
| 2718 | } |
| 2719 | blob_reset(&rndr.notes.all); |
| 2720 | for(i=0; i<rndr.nBlobCache; i++){ |
| 2721 | fossil_free(rndr.aBlobCache[i]); |
| 2722 | } |
| 2723 | } |
| 2724 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -274,11 +274,11 @@ | |
| 274 | return blob_compare(&lra->id, &lrb->id); |
| 275 | } |
| 276 | |
| 277 | /* cmp_footnote_id -- comparison function for footnotes qsort. |
| 278 | * Empty IDs sort last (in undetermined order). |
| 279 | * Equal IDs are sorted in the order of definition in the source */ |
| 280 | static int cmp_footnote_id(const void *fna, const void *fnb){ |
| 281 | const struct footnote *a = fna, *b = fnb; |
| 282 | const int szA = blob_size(&a->id), szB = blob_size(&b->id); |
| 283 | if( szA ){ |
| 284 | if( szB ){ |
| @@ -2498,10 +2498,11 @@ | |
| 2498 | struct link_ref *lr; |
| 2499 | struct footnote *fn; |
| 2500 | size_t i, beg, end = 0; |
| 2501 | struct render rndr; |
| 2502 | Blob text = BLOB_INITIALIZER; /* input after the first pass */ |
| 2503 | Blob *allNotes = &rndr.notes.all; |
| 2504 | |
| 2505 | /* filling the render structure */ |
| 2506 | if( !rndrer ) return; |
| 2507 | rndr.make = *rndrer; |
| 2508 | rndr.nBlobCache = 0; |
| @@ -2565,24 +2566,26 @@ | |
| 2566 | qsort(blob_buffer(&rndr.refs), |
| 2567 | blob_size(&rndr.refs)/sizeof(struct link_ref), |
| 2568 | sizeof(struct link_ref), |
| 2569 | cmp_link_ref_sort); |
| 2570 | } |
| 2571 | rndr.notes.nLbled = COUNT_FOOTNOTES( allNotes ); |
| 2572 | |
| 2573 | /* sort footnotes by ID and join duplicates */ |
| 2574 | if( rndr.notes.nLbled > 1 ){ |
| 2575 | int nDups = 0; |
| 2576 | fn = CAST_AS_FOOTNOTES( allNotes ); |
| 2577 | qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); |
| 2578 | |
| 2579 | /* concatenate footnotes with equal labels */ |
| 2580 | for(i=0; i<rndr.notes.nLbled ;){ |
| 2581 | struct footnote *x = fn + i; |
| 2582 | size_t j = i+1, k = blob_size(&x->text) + 64; |
| 2583 | while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){ |
| 2584 | k += blob_size(&fn[j].text) + 10; |
| 2585 | j++; |
| 2586 | nDups++; |
| 2587 | } |
| 2588 | if( i+1<j ){ |
| 2589 | Blob tmp = empty_blob; |
| 2590 | blob_reserve(&tmp, k); |
| 2591 | blob_append_string(&tmp, "<ul class='footnote-joined'>\n"); |
| @@ -2592,76 +2595,66 @@ | |
| 2595 | blob_append(&tmp, blob_buffer(&y->text), blob_size(&y->text)); |
| 2596 | blob_append_string(&tmp, "</li>\n"); |
| 2597 | |
| 2598 | /* free memory buffer */ |
| 2599 | blob_reset(&y->text); |
| 2600 | if( k!=i ) blob_reset(&y->id); |
| 2601 | } |
| 2602 | blob_append_string(&tmp, "</ul>\n"); |
| 2603 | x->text = tmp; |
| 2604 | } |
| 2605 | i = j; |
| 2606 | } |
| 2607 | if( nDups ){ /* clean rndr.notes.all from invalidated footnotes */ |
| 2608 | const int n = rndr.notes.nLbled - nDups; |
| 2609 | struct Blob filtered = empty_blob; |
| 2610 | blob_reserve(&filtered, n*sizeof(struct footnote)); |
| 2611 | for(i=0; i<rndr.notes.nLbled; i++){ |
| 2612 | if( blob_size(&fn[i].id) ){ |
| 2613 | blob_append(&filtered, (char*)(fn+i), sizeof(struct footnote)); |
| 2614 | } |
| 2615 | } |
| 2616 | blob_reset( allNotes ); |
| 2617 | rndr.notes.all = filtered; |
| 2618 | rndr.notes.nLbled = n; |
| 2619 | assert( COUNT_FOOTNOTES(allNotes) == rndr.notes.nLbled ); |
| 2620 | } |
| 2621 | } |
| 2622 | fn = CAST_AS_FOOTNOTES( allNotes ); |
| 2623 | for(i=0; i<rndr.notes.nLbled; i++){ |
| 2624 | fn[i].index = i; |
| 2625 | } |
| 2626 | assert( rndr.notes.nMarks==0 ); |
| 2627 | |
| 2628 | /* second pass: actual rendering */ |
| 2629 | if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); |
| 2630 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); |
| 2631 | |
| 2632 | if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ |
| 2633 | |
| 2634 | /* Footnotes must be parsed for the correct discovery of (back)links */ |
| 2635 | Blob *notes = new_work_buffer( &rndr ); |
| 2636 | Blob *tmp = new_work_buffer( &rndr ); |
| 2637 | int nMarks = -1; |
| 2638 | |
| 2639 | /* inline notes may get appended to rndr.notes.all while rendering */ |
| 2640 | while(1){ |
| 2641 | struct footnote *aNotes; |
| 2642 | const int N = COUNT_FOOTNOTES( allNotes ); |
| 2643 | |
| 2644 | /* make a shallow copy of `origin` */ |
| 2645 | blob_truncate(notes,0); |
| 2646 | blob_append(notes, blob_buffer(allNotes), blob_size(allNotes)); |
| 2647 | aNotes = CAST_AS_FOOTNOTES(notes); |
| 2648 | qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); |
| 2649 | |
| 2650 | if( nMarks == rndr.notes.nMarks ) break; |
| 2651 | nMarks = rndr.notes.nMarks; |
| 2652 | |
| 2653 | for(i=0; i<N; i++){ |
| 2654 | const int j = aNotes[i].index; |
| 2655 | struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; |
| 2656 | assert( 0<=j && j<N ); |
| 2657 | if( x->bRndred || !x->nUsed ) continue; |
| 2658 | assert( x->iMark > 0 ); |
| 2659 | assert( blob_size(&x->text) ); |
| 2660 | blob_truncate(tmp,0); |
| @@ -2708,16 +2701,16 @@ | |
| 2701 | blob_reset(&lr[i].id); |
| 2702 | blob_reset(&lr[i].link); |
| 2703 | blob_reset(&lr[i].title); |
| 2704 | } |
| 2705 | blob_reset(&rndr.refs); |
| 2706 | fn = CAST_AS_FOOTNOTES( allNotes ); |
| 2707 | end = COUNT_FOOTNOTES( allNotes ); |
| 2708 | for(i=0; i<end; i++){ |
| 2709 | if(blob_size(&fn[i].id)) blob_reset(&fn[i].id); |
| 2710 | blob_reset(&fn[i].text); |
| 2711 | } |
| 2712 | blob_reset(&rndr.notes.all); |
| 2713 | for(i=0; i<rndr.nBlobCache; i++){ |
| 2714 | fossil_free(rndr.aBlobCache[i]); |
| 2715 | } |
| 2716 | } |
| 2717 |