From a27b0ef2090d7b4a36757470dd0c0f5b6357e441 Mon Sep 17 00:00:00 2001 From: yuin Date: Thu, 25 Jul 2019 16:59:07 +0900 Subject: [PATCH] Fix bugs found in fuzzing --- parser/fcode_block.go | 4 +- testutil.go | 35 ++++++++++++--- util/util.go | 102 +++++++++++++++++++++++++++++++----------- 3 files changed, 109 insertions(+), 32 deletions(-) diff --git a/parser/fcode_block.go b/parser/fcode_block.go index 401a6c2..09dc5e4 100644 --- a/parser/fcode_block.go +++ b/parser/fcode_block.go @@ -78,9 +78,9 @@ func (b *fencedCodeBlockParser) Continue(node ast.Node, reader text.Reader, pc C return Close } } + pos, padding := util.DedentPositionPadding(line, reader.LineOffset(), segment.Padding, fdata.indent) - pos, padding := util.DedentPosition(line, fdata.indent) - seg := text.NewSegmentPadding(segment.Start+pos-segment.Padding, segment.Stop, padding) + seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding) node.Lines().Append(seg) reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding) return Continue | NoChildren diff --git a/testutil.go b/testutil.go index 4917e58..43d2d56 100644 --- a/testutil.go +++ b/testutil.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/yuin/goldmark/util" "os" + "runtime/debug" "strconv" "strings" testing "testing" @@ -78,11 +79,31 @@ func DoTestCaseFile(m Markdown, filename string, t *testing.T) { func DoTestCases(m Markdown, cases []MarkdownTestCase, t *testing.T) { for _, testCase := range cases { - var out bytes.Buffer - if err := m.Convert([]byte(testCase.Markdown), &out); err != nil { - panic(err) - } - if !bytes.Equal(bytes.TrimSpace(out.Bytes()), bytes.TrimSpace([]byte(testCase.Expected))) { + DoTestCase(m, testCase, t) + } +} + +func DoTestCase(m Markdown, testCase MarkdownTestCase, t *testing.T) { + var ok bool + var out bytes.Buffer + defer func() { + if err := recover(); err != nil { + format := `============= case %d ================ +Markdown: +----------- +%s + +Expected: +---------- +%s + +Actual +--------- +%v +%s +` + t.Errorf(format, testCase.No, testCase.Markdown, testCase.Expected, err, debug.Stack()) + } else if !ok { format := `============= case %d ================ Markdown: ----------- @@ -98,6 +119,10 @@ Actual ` t.Errorf(format, testCase.No, testCase.Markdown, testCase.Expected, out.Bytes()) } + }() + if err := m.Convert([]byte(testCase.Markdown), &out); err != nil { + panic(err) } + ok = bytes.Equal(bytes.TrimSpace(out.Bytes()), bytes.TrimSpace([]byte(testCase.Expected))) } diff --git a/util/util.go b/util/util.go index 46603d3..de8cd0a 100644 --- a/util/util.go +++ b/util/util.go @@ -87,31 +87,6 @@ func IsBlank(bs []byte) bool { return true } -// DedentPosition dedents lines by the given width. -func DedentPosition(bs []byte, width int) (pos, padding int) { - if width == 0 { - return - } - i := 0 - l := len(bs) - w := 0 - for ; i < l && w < width; i++ { - b := bs[i] - if b == ' ' { - w++ - } else if b == '\t' { - w += 4 - } else { - break - } - } - padding = w - width - if padding < 0 { - padding = 0 - } - return i, padding -} - // VisualizeSpaces visualize invisible space characters. func VisualizeSpaces(bs []byte) []byte { bs = bytes.Replace(bs, []byte(" "), []byte("[SPACE]"), -1) @@ -137,6 +112,9 @@ func TabWidth(currentPos int) int { // width=2 is in the tab character. In this case, IndentPosition returns // (pos=1, padding=2) func IndentPosition(bs []byte, currentPos, width int) (pos, padding int) { + if width == 0 { + return 0, 0 + } w := 0 l := len(bs) i := 0 @@ -160,6 +138,80 @@ func IndentPosition(bs []byte, currentPos, width int) (pos, padding int) { return -1, -1 } +// IndentPositionPadding searches an indent position with the given width for the given line. +// This function is mostly same as IndentPosition except this function +// takes account into additional paddings. +func IndentPositionPadding(bs []byte, currentPos, paddingv, width int) (pos, padding int) { + if width == 0 { + return 0, paddingv + } + w := 0 + i := 0 + l := len(bs) + for ; i < l; i++ { + if bs[i] == '\t' { + w += TabWidth(currentPos + w) + } else if bs[i] == ' ' { + w++ + } else { + break + } + } + if w >= width { + return i - paddingv, w - width + } + return -1, -1 +} + +// DedentPosition dedents lines by the given width. +func DedentPosition(bs []byte, currentPos, width int) (pos, padding int) { + if width == 0 { + return 0, 0 + } + w := 0 + l := len(bs) + i := 0 + for ; i < l; i++ { + if bs[i] == '\t' { + w += TabWidth(currentPos + w) + } else if bs[i] == ' ' { + w++ + } else { + break + } + } + if w >= width { + return i, w - width + } + return i, 0 +} + +// DedentPositionPadding dedents lines by the given width. +// This function is mostly same as DedentPosition except this function +// takes account into additional paddings. +func DedentPositionPadding(bs []byte, currentPos, paddingv, width int) (pos, padding int) { + if width == 0 { + return 0, paddingv + } + + w := 0 + i := 0 + l := len(bs) + for ; i < l; i++ { + if bs[i] == '\t' { + w += TabWidth(currentPos + w) + } else if bs[i] == ' ' { + w++ + } else { + break + } + } + if w >= width { + return i - paddingv, w - width + } + return i - paddingv, 0 +} + // IndentWidth calculate an indent width for the given line. func IndentWidth(bs []byte, currentPos int) (width, pos int) { l := len(bs)