constantine/bindings/gen_header.nim

211 lines
5.9 KiB
Nim

# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
std/[strformat, strutils, macros, strtabs]
# Overview
# ------------------------------------------------------------
#
# This files provides template for C header generation
proc genHeaderLicense*(): string =
"""
/*
* Constantine
* Copyright (c) 2018-2019 Status Research & Development GmbH
* Copyright (c) 2020-Present Mamy André-Ratsimbazafy
* Licensed and distributed under either of
* * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
* * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
* at your option. This file may not be copied, modified, or distributed except according to those terms.
*/
"""
proc genHeader*(name, body: string): string =
&"""
#ifndef __CTT_H_{name}__
#define __CTT_H_{name}__
{body}
#endif
"""
proc genCpp*(body: string): string {.raises:[ValueError].} =
&"""
#ifdef __cplusplus
extern "C" {{
#endif
{body}
#ifdef __cplusplus
}}
#endif
"""
# Types' declarations
# -------------------------------------------
proc genBuiltinsTypes*(): string =
"""
#if defined(__SIZE_TYPE__) && defined(__PTRDIFF_TYPE__)
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
#else
#include <stddef.h>
#endif
#if defined(__UINT8_TYPE__) && defined(__UINT32_TYPE__) && defined(__UINT64_TYPE__)
typedef __UINT8_TYPE__ uint8_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
#else
#include <stdint.h>
#endif
"""
proc genCttBaseTypedef*(): string =
"""
typedef size_t secret_word;
typedef size_t secret_bool;
typedef uint8_t byte;
"""
proc genWordsRequired*(): string =
"""
#define WordBitWidth (sizeof(secret_word)*8)
#define words_required(bits) ((bits+WordBitWidth-1)/WordBitWidth)
"""
proc genField*(name: string, bits: int): string =
&"typedef struct {{ secret_word limbs[words_required({bits})]; }} {name};"
proc genExtField*(name: string, degree: int, basename: string): string =
&"typedef struct {{ {basename} c[{degree}]; }} {name};"
proc genEllipticCurvePoint*(name, coords, basename: string): string =
&"typedef struct {{ {basename} {coords}; }} {name};"
# Nim internals
# -------------------------------------------
proc declNimMain*(libName: string): string =
## Create the NimMain function.
## It initializes:
## - the Nim runtime if seqs, strings or heap-allocated types are used,
## this is the case only if Constantine is multithreaded.
## - runtime CPU features detection
##
## Assumes library is compiled with --nimMainPrefix:ctt_{libName}_
&"""
/*
* Initializes the library:
* - detect CPU features like ADX instructions support (MULX, ADCX, ADOX)
*/
void ctt_{libName}_init_NimMain(void);"""
# Subroutines' declarations
# -------------------------------------------
let TypeMap {.compileTime.} = newStringTable({
"SecretBool": "secret_bool",
"SecretWord": "secret_word"
})
proc toCrettype(node: NimNode): string =
node.expectKind({nnkEmpty, nnkSym})
if node.kind == nnkEmpty:
# align iwth secret_bool and secret_word
"void "
else:
TypeMap[$node]
proc toCtrivialParam(name: string, typ: NimNode): string =
typ.expectKind({nnkVarTy, nnkSym})
let isVar = typ.kind == nnkVarTy
let constify = if isVar: ""
else: "const "
let sTyp = if isVar: $typ[0]
else: $typ
if sTyp in TypeMap:
# Pass-by-value
constify & TypeMap[sTyp] & " " & name
else:
# Pass-by-reference
constify & sTyp & "* " & name
proc toCparam(name: string, typ: NimNode): string =
typ.expectKind({nnkVarTy, nnkCall, nnkSym})
if typ.kind == nnkCall:
typ[0].expectKind(nnkOpenSymChoice)
doAssert typ[0][0].eqIdent"[]"
doAssert typ[1].eqIdent"openArray"
let sTyp = $typ[2]
if sTyp in TypeMap:
"const " & TypeMap[sTyp] & " " & name & "[], ptrdiff_t " & name & "_len"
else:
"const " & sTyp & " " & name & "[], ptrdiff_t " & name & "_len"
elif typ.kind == nnkVarTy and typ[0].kind == nnkCall:
typ[0][0].expectKind(nnkOpenSymChoice)
doAssert typ[0][0][0].eqIdent"[]"
doAssert typ[0][1].eqIdent"openArray"
let sTyp = $typ[0][2]
if sTyp in TypeMap:
TypeMap[sTyp] & " " & name & "[], ptrdiff_t " & name & "_len"
else:
sTyp & " " & name & "[], ptrdiff_t " & name & "_len"
else:
toCtrivialParam(name, typ)
macro collectBindings*(cBindingsStr: untyped, body: typed): untyped =
## Collect function definitions from a generator template
body.expectKind(nnkStmtList)
var cBindings: string
for generator in body:
generator.expectKind(nnkStmtList)
for fnDef in generator:
if fnDef.kind notin {nnkProcDef, nnkFuncDef}:
continue
cBindings &= "\n"
# rettype name(pType0* pName0, pType1* pName1, ...);
cBindings &= fnDef.params[0].toCrettype()
cBindings &= ' '
cBindings &= $fnDef.name
cBindings &= '('
for i in 1 ..< fnDef.params.len:
if i != 1: cBindings &= ", "
let paramDef = fnDef.params[i]
paramDef.expectKind(nnkIdentDefs)
let pType = paramDef[^2]
# No default value
paramDef[^1].expectKind(nnkEmpty)
for j in 0 ..< paramDef.len - 2:
if j != 0: cBindings &= ", "
var name = $paramDef[j]
cBindings &= toCparam(name.split('`')[0], pType)
cBindings &= ");"
if defined(CttGenerateHeaders):
result = newConstStmt(cBindingsStr, newLit cBindings)
else:
result = body