summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/deps/picotls/deps/cifra/doc/build.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/server/h2o/libh2o/deps/picotls/deps/cifra/doc/build.py274
1 files changed, 274 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/picotls/deps/cifra/doc/build.py b/web/server/h2o/libh2o/deps/picotls/deps/cifra/doc/build.py
new file mode 100644
index 00000000..0b006825
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/picotls/deps/cifra/doc/build.py
@@ -0,0 +1,274 @@
+"""
+Extracts documentation from cifra headers.
+
+We want to embed documentation in headers for good
+locality. But want to write rst for formatting by sphinx.
+This is a problem.
+
+'Breathe' provides a bridge between doxygen and sphinx,
+but doxygen's documentation markup is pretty awful.
+
+Therefore, we write rst directly in C headers, and then
+extract it here. The rules are: a C block comment
+starting exactly '/* .. ' is dedented, and then included
+verbatim. The convention is that the documentation
+preceeds the C declarations they apply to, and that struct
+members are documented before the struct they are contained
+within. eg:
+
+/* .. c:function:: int foo(int bar)
+ * Foos a bar, returning the foo coefficient.
+ */
+int foo(int bar);
+
+/* .. c:type:: thing
+ * Container for things.
+ *
+ * .. c:member:: int thing.foo
+ * Count of foos.
+ *
+ * .. c:member:: int thing.bar
+ * Count of bars.
+ */
+typedef struct
+{
+ int foo;
+ int bar;
+} thing;
+
+As a special effect, the following tokens are replaced
+in the comments:
+
+ - $DECL: the immediately following function declaration
+"""
+
+import glob
+import re
+import StringIO
+
+# We know which headers constitute the external interface.
+EXTERNAL = """
+aes
+cf_config
+chash
+hmac
+modes
+pbkdf2
+prp
+salsa20
+sha1
+sha2
+sha3
+norx
+poly1305
+chacha20poly1305
+drbg
+""".split()
+
+# Basic idea of a C identifier
+C_IDENTIFIER = '[a-zA-Z_][a-zA-Z0-9_]+'
+
+DECL_RE = re.compile(r'^\s*(.+?' + C_IDENTIFIER + '\(.+?\));', re.MULTILINE | re.DOTALL)
+COMMENT_RE = re.compile(r'^\s*\/\* (\.\..*?) \*\/$', re.MULTILINE | re.DOTALL)
+INTRO_RE = re.compile(r'^\s*\/\*\*(.*?)\*\/$', re.MULTILINE | re.DOTALL)
+NEW_SECTION_RE = re.compile(r'^..+\n(==+|--+)$', re.MULTILINE)
+
+class section(object):
+ def __init__(self):
+ self.intro = []
+ self.macros = []
+ self.types = []
+ self.functions = []
+ self.values = []
+
+ def __repr__(self):
+ return repr(self.format()[:30])
+
+ def __str__(self):
+ return repr(self.format()[:30])
+
+ def format(self):
+ f = StringIO.StringIO()
+
+ def emit(title, section):
+ if len(section) == 0:
+ return
+
+ if title:
+ print >>f, title
+ print >>f, '*' * len(title)
+ print >>f
+
+ for s in section:
+ print >>f, s.getvalue()
+
+ emit(None, self.intro)
+ emit('Macros', self.macros)
+ emit('Types', self.types)
+ emit('Functions', self.functions)
+ emit('Values', self.values)
+
+ return f.getvalue()
+
+ def is_empty(self):
+ items = len(self.intro) + len(self.macros) + len(self.types) + \
+ len(self.functions) + len(self.values)
+ return 0 == items
+
+ def add_item(self, sec):
+ f = StringIO.StringIO()
+ sec.append(f)
+ return f
+
+def massage_decl(decl):
+ """
+ Tart-up a C function declaration: remove storage qualifiers,
+ smush onto one line, escape asterisks.
+ """
+ for storage in 'extern static inline'.split():
+ decl = decl.replace(storage + ' ', '')
+
+ fixed_lines = ' '.join(line.strip() for line in decl.splitlines())
+
+ return fixed_lines.replace('*', '\\*')
+
+def replace_decl(comment, comment_match, header):
+ if '$DECL' not in comment:
+ return comment
+
+ start = comment_match.end(0) + 1
+ decl_match = DECL_RE.match(header, start)
+
+ if decl_match is None:
+ print 'Cause:', comment
+ print 'Trailer:', header[start:start+60]
+ raise IOError('$DECL present but cannot find following DECL')
+
+ decl = decl_match.group(1)
+ decl = massage_decl(decl)
+ return comment.replace('$DECL', decl)
+
+def decomment(lines):
+ for i in range(len(lines)):
+ if lines[i].startswith(' *'):
+ lines[i] = lines[i][3:]
+ lines[i] = lines[i].strip()
+
+def drop_empty_prefix(lines):
+ while len(lines) and lines[0] == '':
+ lines.pop(0)
+
+def starts_new_section(lines):
+ txt = '\n'.join(lines)
+ r = NEW_SECTION_RE.search(txt) is not None
+ return r
+
+def process(header, rst):
+ """
+ Converts a header into restructured text.
+
+ header is a file-like opened to read the header.
+ rst is a file-like opened to write the rst results.
+ """
+
+ hh = header.read()
+
+ # Collect definitions into sections
+ sec = None
+ all_sections = []
+ intro, macros, types, functions, values = [], [], [], [], []
+
+ def add_section():
+ if sec and not sec.is_empty():
+ all_sections.append(sec)
+ return section()
+
+ sec = add_section()
+
+ offs = 0
+
+ while True:
+ intro_match = INTRO_RE.search(hh, offs)
+ comment_match = COMMENT_RE.search(hh, offs)
+
+ if intro_match is None and comment_match is None:
+ break
+
+ # process earliest occuring
+ if intro_match is not None and (comment_match is None or intro_match.start(0) < comment_match.start(0)):
+ txt = intro_match.group(1)
+
+ lines = txt.splitlines()
+ decomment(lines)
+ drop_empty_prefix(lines)
+
+ if starts_new_section(lines):
+ sec = add_section()
+
+ outf = sec.add_item(sec.intro)
+
+ for l in lines:
+ print >>outf, l
+ offs = intro_match.end(0) + 1
+ continue
+
+ if comment_match is not None and (intro_match is None or comment_match.start(0) < intro_match.start(0)):
+ txt = comment_match.group(1)
+
+ # work out which section this goes into
+ outf = None
+ if '.. c:macro::' in txt:
+ outf = sec.add_item(sec.macros)
+ elif '.. c:type::' in txt:
+ outf = sec.add_item(sec.types)
+ elif '.. c:var::' in txt:
+ outf = sec.add_item(sec.values)
+ elif '.. c:function::' in txt:
+ outf = sec.add_item(sec.functions)
+ elif '.. c:member::' in txt:
+ if len(types) == 0:
+ raise IOError('c:member must come after a c:type')
+ outf = types[-1]
+ else:
+ raise IOError('Cannot categorise item: ' + txt)
+
+ # expand $DECL
+ txt = replace_decl(txt, comment_match, hh)
+
+ # decomment lines
+ lines = txt.splitlines()
+ decomment(lines)
+
+ # domain lines are unindented
+ while lines and lines[0].startswith('.. '):
+ print >>outf, lines.pop(0)
+ print >>outf
+
+ # empty prefix lines are stripped
+ drop_empty_prefix(lines)
+
+ # other lines are indented
+ for line in lines:
+ if len(line):
+ line = ' ' + line
+ print >>outf, line
+
+ offs = comment_match.end(0) + 1
+ continue
+
+ add_section()
+
+ for sec in all_sections:
+ rst.write(sec.format())
+
+def run():
+ for fn in EXTERNAL:
+ print '** build', fn
+ header = '../src/' + fn + '.h'
+ rst = fn + '.rst'
+ with open(header, 'r') as fh:
+ with open(rst, 'w') as fr:
+ process(fh, fr)
+
+if __name__ == '__main__':
+ run()