1//go:build cgo
2
3package clib
4
5/*
6#include "md4c.h"
7#include "md4c-html.h"
8#include <stdlib.h>
9#include <string.h>
10
11// Buffer for collecting md4c-html output.
12typedef struct {
13 char* data;
14 size_t len;
15 size_t cap;
16} MdBuf;
17
18static void md_output_cb(const MD_CHAR* text, MD_SIZE size, void* userdata) {
19 MdBuf* b = (MdBuf*)userdata;
20 size_t needed = b->len + size;
21 if (needed > b->cap) {
22 size_t newcap = b->cap ? b->cap * 2 : 4096;
23 while (newcap < needed) newcap *= 2;
24 b->data = (char*)realloc(b->data, newcap);
25 b->cap = newcap;
26 }
27 memcpy(b->data + b->len, text, size);
28 b->len += size;
29}
30
31// md4c_to_html converts Markdown to HTML using md4c.
32// Returns the HTML string (caller must free) and sets *out_len.
33// Returns NULL on failure.
34static char* md4c_to_html(const char* input, size_t input_len, size_t* out_len) {
35 MdBuf buf = {0};
36 buf.cap = input_len * 2;
37 if (buf.cap < 256) buf.cap = 256;
38 buf.data = (char*)malloc(buf.cap);
39 if (!buf.data) return NULL;
40
41 // Use permissive flags to handle raw HTML in emails.
42 unsigned parser_flags = MD_FLAG_PERMISSIVEAUTOLINKS |
43 MD_FLAG_TABLES |
44 MD_FLAG_STRIKETHROUGH |
45 MD_FLAG_TASKLISTS;
46
47 int ret = md_html(input, (MD_SIZE)input_len, md_output_cb, &buf,
48 parser_flags, 0);
49 if (ret != 0) {
50 free(buf.data);
51 return NULL;
52 }
53
54 *out_len = buf.len;
55 return buf.data;
56}
57*/
58import "C"
59import (
60 "log"
61 "unsafe"
62)
63
64// MarkdownToHTML converts Markdown bytes to HTML using md4c (C).
65// This is significantly faster than goldmark for large documents.
66func MarkdownToHTML(md []byte) []byte {
67 if len(md) == 0 {
68 return nil
69 }
70
71 cInput := C.CBytes(md)
72 defer C.free(cInput)
73
74 var outLen C.size_t
75 result := C.md4c_to_html((*C.char)(cInput), C.size_t(len(md)), &outLen)
76 if result == nil {
77 log.Printf("markdown: md4c_to_html failed, falling back to escaped plain-text HTML")
78 return markdownPlainTextHTML(md)
79 }
80 defer C.free(unsafe.Pointer(result))
81
82 return C.GoBytes(unsafe.Pointer(result), C.int(outLen))
83}