Skip to content

Commit 83edbd3

Browse files
authored
Merge pull request #2583 from DeusData/fix/rst-body-cut
fix(rst): a section's text no longer reads an unwritten byte
2 parents 76a2485 + 128e3d8 commit 83edbd3

2 files changed

Lines changed: 44 additions & 10 deletions

File tree

‎internal/cbm/doclink_rst.c‎

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,18 +1606,19 @@ static const char *rst_body(rst_doc_t *d, int from, int to) {
16061606
return NULL;
16071607
}
16081608
int w = 0;
1609-
for (int k = from; k < to && w < RST_BODY_MAX; k++) {
1609+
bool full = false; /* the next character did not fit: the text ends before it */
1610+
for (int k = from; k < to && !full; k++) {
16101611
const char *s = d->L[k].s;
16111612
int len = d->L[k].len;
16121613
int i = 0;
1613-
while (i < len && w < RST_BODY_MAX) {
1614+
while (i < len && !full) {
16141615
int n;
16151616
uint32_t cp = rst_cp(s, len, i, &n);
16161617
if (rst_space_cp(cp)) {
16171618
i += n;
16181619
continue;
16191620
}
1620-
if (w > 0) {
1621+
if (w > 0 && w < RST_BODY_MAX) {
16211622
out[w++] = ' ';
16221623
}
16231624
while (i < len) {
@@ -1626,7 +1627,7 @@ static const char *rst_body(rst_doc_t *d, int from, int to) {
16261627
break;
16271628
}
16281629
if (w + n > RST_BODY_MAX) {
1629-
w = RST_BODY_MAX + 1; /* stop: the next character does not fit */
1630+
full = true; /* only whole characters are written: none is cut */
16301631
break;
16311632
}
16321633
memcpy(out + w, s + i, (size_t)n);
@@ -1635,12 +1636,6 @@ static const char *rst_body(rst_doc_t *d, int from, int to) {
16351636
}
16361637
}
16371638
}
1638-
if (w > RST_BODY_MAX) {
1639-
w = RST_BODY_MAX;
1640-
while (w > 0 && ((unsigned char)out[w] & 0xC0) == 0x80) {
1641-
w--;
1642-
}
1643-
}
16441639
while (w > 0 && out[w - 1] == ' ') {
16451640
w--;
16461641
}

‎tests/test_doc_links_rst.c‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,9 +558,48 @@ TEST(rst_ship_gate) {
558558
PASS();
559559
}
560560

561+
/* A section's text is cut before the first character that does not fit its
562+
* 500 bytes: only whole characters are written, and the text ends at the last
563+
* one written (it once tested an unwritten byte past the end, so where the
564+
* text ended depended on what that memory held). */
565+
TEST(rst_section_text_cut) {
566+
static const struct {
567+
const char *tail;
568+
const char *want_tail;
569+
} cases[] = {
570+
{" xxxxx tail", " xxxxx"}, /* a word that ends at byte 500 */
571+
{" xxxx\xc3\xa9 more", " xxxx"}, /* a two-byte character that would cross it */
572+
{" yyyyyyyy", " yyyyy"}, /* a word cut at a character */
573+
};
574+
for (size_t c = 0; c < sizeof(cases) / sizeof(cases[0]); c++) {
575+
char doc[1024];
576+
char want[600];
577+
int n = snprintf(doc, sizeof(doc), "Title\n=====\n\n");
578+
int w = 0;
579+
for (int k = 0; k < 99; k++) { /* 99 words: 494 bytes, nine per paragraph */
580+
n += snprintf(doc + n, sizeof(doc) - (size_t)n, "%sabcd",
581+
k == 0 ? ""
582+
: k % 9 == 0 ? "\n\n"
583+
: " ");
584+
w += snprintf(want + w, sizeof(want) - (size_t)w, "%sabcd", k ? " " : "");
585+
}
586+
snprintf(doc + n, sizeof(doc) - (size_t)n, "%s\n", cases[c].tail);
587+
snprintf(want + w, sizeof(want) - (size_t)w, "%s", cases[c].want_tail);
588+
CBMFileResult *r = dm_extract(doc, CBM_LANG_RST, "docs/cut.rst");
589+
ASSERT_NOT_NULL(r);
590+
const CBMDefinition *t = rst_def(r, "Section", "Title");
591+
ASSERT_NOT_NULL(t);
592+
ASSERT_NOT_NULL(t->docstring);
593+
ASSERT_STR_EQ(t->docstring, want);
594+
cbm_free_result(r);
595+
}
596+
PASS();
597+
}
598+
561599
SUITE(doc_links_rst) {
562600
dm_ship_held_families();
563601
RUN_TEST(rst_scan_structure);
602+
RUN_TEST(rst_section_text_cut);
564603
RUN_TEST(rst_python_scope_blob);
565604
RUN_TEST(rst_links_pipeline);
566605
RUN_TEST(rst_c_member_owner);

0 commit comments

Comments
 (0)