func entity(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	data = data[offset:]
	end := 1
	if end < len(data) && data[end] == '#' {
		end++
	}
	for end < len(data) && isalnum(data[end]) {
		end++
	}
	if end < len(data) && data[end] == ';' {
		end++  
	} else {
		return 0  
	}
	p.r.Entity(out, data[:end])
	return end
}
'&' escaped when it doesn't belong to an entity
valid entities are assumed to be anything matching &#?[A-Za-z0-9]+;
package blackfriday
import (
	"bytes"
	"regexp"
	"strconv"
)
var (
	urlRe    = `((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+`
	anchorRe = regexp.MustCompile(`^(<a\shref="` + urlRe + `"(\stitle="[^"<>]+")?\s?>` + urlRe + `<\/a>)`)
)
// Functions to parse text within a block
// Each function returns the number of chars taken care of
// data is the complete block being rendered
// offset is the number of valid chars before the current cursor
func (p *parser) inline(out *bytes.Buffer, data []byte) {
	// this is called recursively: enforce a maximum depth
	if p.nesting >= p.maxNesting {
		return
	}
	p.nesting++
	i, end := 0, 0
	for i < len(data) {
		// copy inactive chars into the output
		for end < len(data) && p.inlineCallback[data[end]] == nil {
			end++
		}
		p.r.NormalText(out, data[i:end])
		if end >= len(data) {
			break
		}
		i = end
		// call the trigger
		handler := p.inlineCallback[data[end]]
		if consumed := handler(p, out, data, i); consumed == 0 {
			// no action from the callback; buffer the byte for later
			end = i + 1
		} else {
			// skip past whatever the callback used
			i += consumed
			end = i
		}
	}
	p.nesting--
}
// single and double emphasis parsing
func emphasis(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	data = data[offset:]
	c := data[0]
	ret := 0
	if len(data) > 2 && data[1] != c {
		// whitespace cannot follow an opening emphasis;
		// strikethrough only takes two characters '~~'
		if c == '~' || isspace(data[1]) {
			return 0
		}
		if ret = helperEmphasis(p, out, data[1:], c); ret == 0 {
			return 0
		}
		return ret + 1
	}
	if len(data) > 3 && data[1] == c && data[2] != c {
		if isspace(data[2]) {
			return 0
		}
		if ret = helperDoubleEmphasis(p, out, data[2:], c); ret == 0 {
			return 0
		}
		return ret + 2
	}
	if len(data) > 4 && data[1] == c && data[2] == c && data[3] != c {
		if c == '~' || isspace(data[3]) {
			return 0
		}
		if ret = helperTripleEmphasis(p, out, data, 3, c); ret == 0 {
			return 0
		}
		return ret + 3
	}
	return 0
}
func codeSpan(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	data = data[offset:]
	nb := 0
	// count the number of backticks in the delimiter
	for nb < len(data) && data[nb] == '`' {
		nb++
	}
	// find the next delimiter
	i, end := 0, 0
	for end = nb; end < len(data) && i < nb; end++ {
		if data[end] == '`' {
			i++
		} else {
			i = 0
		}
	}
	// no matching delimiter?
	if i < nb && end >= len(data) {
		return 0
	}
	// trim outside whitespace
	fBegin := nb
	for fBegin < end && data[fBegin] == ' ' {
		fBegin++
	}
	fEnd := end - nb
	for fEnd > fBegin && data[fEnd-1] == ' ' {
		fEnd--
	}
	// render the code span
	if fBegin != fEnd {
		p.r.CodeSpan(out, data[fBegin:fEnd])
	}
	return end
}
// newline preceded by two spaces becomes <br>
// newline without two spaces works when EXTENSION_HARD_LINE_BREAK is enabled
func lineBreak(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	// remove trailing spaces from out
	outBytes := out.Bytes()
	end := len(outBytes)
	eol := end
	for eol > 0 && outBytes[eol-1] == ' ' {
		eol--
	}
	out.Truncate(eol)
	precededByTwoSpaces := offset >= 2 && data[offset-2] == ' ' && data[offset-1] == ' '
	precededByBackslash := offset >= 1 && data[offset-1] == '\\' // see http://spec.commonmark.org/0.18/#example-527
	precededByBackslash = precededByBackslash && p.flags&EXTENSION_BACKSLASH_LINE_BREAK != 0
	if p.flags&EXTENSION_JOIN_LINES != 0 {
		return 1
	}
	// should there be a hard line break here?
	if p.flags&EXTENSION_HARD_LINE_BREAK == 0 && !precededByTwoSpaces && !precededByBackslash {
		return 0
	}
	if precededByBackslash && eol > 0 {
		out.Truncate(eol - 1)
	}
	p.r.LineBreak(out)
	return 1
}
type linkType int
const (
	linkNormal linkType = iota
	linkImg
	linkDeferredFootnote
	linkInlineFootnote
)
func isReferenceStyleLink(data []byte, pos int, t linkType) bool {
	if t == linkDeferredFootnote {
		return false
	}
	return pos < len(data)-1 && data[pos] == '[' && data[pos+1] != '^'
}
// '[': parse a link or an image or a footnote
func link(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	// no links allowed inside regular links, footnote, and deferred footnotes
	if p.insideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') {
		return 0
	}
	var t linkType
	switch {
	// special case: ![^text] == deferred footnote (that follows something with
	// an exclamation point)
	case p.flags&EXTENSION_FOOTNOTES != 0 && len(data)-1 > offset && data[offset+1] == '^':
		t = linkDeferredFootnote
	// ![alt] == image
	case offset > 0 && data[offset-1] == '!':
		t = linkImg
	// ^[text] == inline footnote
	// [^refId] == deferred footnote
	case p.flags&EXTENSION_FOOTNOTES != 0:
		if offset > 0 && data[offset-1] == '^' {
			t = linkInlineFootnote
		} else if len(data)-1 > offset && data[offset+1] == '^' {
			t = linkDeferredFootnote
		}
	// [text] == regular link
	default:
		t = linkNormal
	}
	data = data[offset:]
	var (
		i                       = 1
		noteId                  int
		title, link, altContent []byte
		textHasNl               = false
	)
	if t == linkDeferredFootnote {
		i++
	}
	brace := 0
	// look for the matching closing bracket
	for level := 1; level > 0 && i < len(data); i++ {
		switch {
		case data[i] == '\n':
			textHasNl = true
		case data[i-1] == '\\':
			continue
		case data[i] == '[':
			level++
		case data[i] == ']':
			level--
			if level <= 0 {
				i-- // compensate for extra i++ in for loop
			}
		}
	}
	if i >= len(data) {
		return 0
	}
	txtE := i
	i++
	// skip any amount of whitespace or newline
	// (this is much more lax than original markdown syntax)
	for i < len(data) && isspace(data[i]) {
		i++
	}
	switch {
	// inline style link
	case i < len(data) && data[i] == '(':
		// skip initial whitespace
		i++
		for i < len(data) && isspace(data[i]) {
			i++
		}
		linkB := i
		// look for link end: ' " ), check for new opening braces and take this
	findlinkend:
		for i < len(data) {
			switch {
			case data[i] == '\\':
				i += 2
			case data[i] == '(':
				brace++
				i++
			case data[i] == ')':
				if brace <= 0 {
					break findlinkend
				}
				brace--
				i++
			case data[i] == '\'' || data[i] == '"':
				break findlinkend
			default:
				i++
			}
		}
		if i >= len(data) {
			return 0
		}
		linkE := i
		titleB, titleE := 0, 0
		if data[i] == '\'' || data[i] == '"' {
			i++
			titleB = i
		findtitleend:
			for i < len(data) {
				switch {
				case data[i] == '\\':
					i += 2
				case data[i] == ')':
					break findtitleend
				default:
					i++
				}
			}
			if i >= len(data) {
				return 0
			}
			titleE = i - 1
			for titleE > titleB && isspace(data[titleE]) {
				titleE--
			}
			if data[titleE] != '\'' && data[titleE] != '"' {
				titleB, titleE = 0, 0
				linkE = i
			}
		}
		for linkE > linkB && isspace(data[linkE-1]) {
			linkE--
		}
		if data[linkB] == '<' {
			linkB++
		}
		if data[linkE-1] == '>' {
			linkE--
		}
		if linkE > linkB {
			link = data[linkB:linkE]
		}
		if titleE > titleB {
			title = data[titleB:titleE]
		}
		i++
	case isReferenceStyleLink(data, i, t):
		var id []byte
		altContentConsidered := false
		i++
		linkB := i
		for i < len(data) && data[i] != ']' {
			i++
		}
		if i >= len(data) {
			return 0
		}
		linkE := i
		if linkB == linkE {
			if textHasNl {
				var b bytes.Buffer
				for j := 1; j < txtE; j++ {
					switch {
					case data[j] != '\n':
						b.WriteByte(data[j])
					case data[j-1] != ' ':
						b.WriteByte(' ')
					}
				}
				id = b.Bytes()
			} else {
				id = data[1:txtE]
				altContentConsidered = true
			}
		} else {
			id = data[linkB:linkE]
		}
		lr, ok := p.getRef(string(id))
		if !ok {
			return 0
		}
		link = lr.link
		title = lr.title
		if altContentConsidered {
			altContent = lr.text
		}
		i++
	default:
		var id []byte
		if textHasNl {
			var b bytes.Buffer
			for j := 1; j < txtE; j++ {
				switch {
				case data[j] != '\n':
					b.WriteByte(data[j])
				case data[j-1] != ' ':
					b.WriteByte(' ')
				}
			}
			id = b.Bytes()
		} else {
			if t == linkDeferredFootnote {
				id = data[2:txtE]  
			} else {
				id = data[1:txtE]
			}
		}
		if t == linkInlineFootnote {
			noteId = len(p.notes) + 1
			var fragment []byte
			if len(id) > 0 {
				if len(id) < 16 {
					fragment = make([]byte, len(id))
				} else {
					fragment = make([]byte, 16)
				}
				copy(fragment, slugify(id))
			} else {
				fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteId))...)
			}
			ref := &reference{
				noteId:   noteId,
				hasBlock: false,
				link:     fragment,
				title:    id,
			}
			p.notes = append(p.notes, ref)
			link = ref.link
			title = ref.title
		} else {
			lr, ok := p.getRef(string(id))
			if !ok {
				return 0
			}
			if t == linkDeferredFootnote {
				lr.noteId = len(p.notes) + 1
				p.notes = append(p.notes, lr)
			}
			link = lr.link
			title = lr.title
			noteId = lr.noteId
		}
		i = txtE + 1
	}
	var content bytes.Buffer
	if txtE > 1 {
		if t == linkImg {
			content.Write(data[1:txtE])
		} else {
			insideLink := p.insideLink
			p.insideLink = true
			p.inline(&content, data[1:txtE])
			p.insideLink = insideLink
		}
	}
	var uLink []byte
	if t == linkNormal || t == linkImg {
		if len(link) > 0 {
			var uLinkBuf bytes.Buffer
			unescapeText(&uLinkBuf, link)
			uLink = uLinkBuf.Bytes()
		}
		if len(uLink) == 0 || (t == linkNormal && content.Len() == 0) {
			return 0
		}
	}
	switch t {
	case linkNormal:
		if len(altContent) > 0 {
			p.r.Link(out, uLink, title, altContent)
		} else {
			p.r.Link(out, uLink, title, content.Bytes())
		}
	case linkImg:
		outSize := out.Len()
		outBytes := out.Bytes()
		if outSize > 0 && outBytes[outSize-1] == '!' {
			out.Truncate(outSize - 1)
		}
		p.r.Image(out, uLink, title, content.Bytes())
	case linkInlineFootnote:
		outSize := out.Len()
		outBytes := out.Bytes()
		if outSize > 0 && outBytes[outSize-1] == '^' {
			out.Truncate(outSize - 1)
		}
		p.r.FootnoteRef(out, link, noteId)
	case linkDeferredFootnote:
		p.r.FootnoteRef(out, link, noteId)
	default:
		return 0
	}
	return i
}
func (p *parser) inlineHTMLComment(out *bytes.Buffer, data []byte) int {
	if len(data) < 5 {
		return 0
	}
	if data[0] != '<' || data[1] != '!' || data[2] != '-' || data[3] != '-' {
		return 0
	}
	i := 5
	for i < len(data) && !(data[i-2] == '-' && data[i-1] == '-' && data[i] == '>') {
		i++
	}
	if i >= len(data) {
		return 0
	}
	return i + 1
}
func leftAngle(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	data = data[offset:]
	altype := LINK_TYPE_NOT_AUTOLINK
	end := tagLength(data, &altype)
	if size := p.inlineHTMLComment(out, data); size > 0 {
		end = size
	}
	if end > 2 {
		if altype != LINK_TYPE_NOT_AUTOLINK {
			var uLink bytes.Buffer
			unescapeText(&uLink, data[1:end+1-2])
			if uLink.Len() > 0 {
				p.r.AutoLink(out, uLink.Bytes(), altype)
			}
		} else {
			p.r.RawHtmlTag(out, data[:end])
		}
	}
	return end
}
var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~")
func escape(p *parser, out *bytes.Buffer, data []byte, offset int) int {
	data = data[offset:]
	if len(data) > 1 {
		if bytes.IndexByte(escapeChars, data[1]) < 0 {
			return 0
		}
		p.r.NormalText(out, data[1:2])
	}
	return 2
}
func unescapeText(ob *bytes.Buffer, src []byte) {
	i := 0
	for i < len(src) {
		org := i
		for i < len(src) && src[i] != '\\' {
			i++
		}
		if i > org {
			ob.Write(src[org:i])
		}
		if i+1 >= len(src) {
			break
		}
		ob.WriteByte(src[i+1])
		i += 2
	}
}