1 ///
2 module ssll;
3 
4 import std.string : toStringz;
5 import std.exception : enforce;
6 
7 version (Posix)
8 {
9     import core.sys.posix.dlfcn;
10 }
11 else version (Windows)
12 {
13     import core.sys.windows.winbase;
14     import core.sys.windows.windef : HINSTANCE;
15 }
16 else static assert(0, "unknown platform");
17 
18 struct ApiUDA { string libname; }
19 auto api(string lname="lib") { return ApiUDA(lname); }
20 
21 string apiFunctionPointerName(string f) { return "__"~f~"_dlg"; }
22 
23 version (Posix) alias LibHandler = void*;
24 version (Windows) alias LibHandler = HINSTANCE;
25 
26 ///
27 LibHandler loadLibrary(string name)
28 {
29     if (name.length == 0) return null;
30 
31     version (Posix)
32         return dlopen(name.toStringz, RTLD_LAZY);
33     version (Windows)
34     {
35         import core.stdc.stdlib : free, malloc;
36 
37         import core.sys.windows.winnls : CP_UTF8, MultiByteToWideChar;
38         import core.sys.windows.winnt : WCHAR;
39 
40         auto len = MultiByteToWideChar(CP_UTF8, 0, name.ptr,
41                                 cast(int)name.length, null, 0);
42 
43         if (len == 0) return null;
44 
45         auto buf = cast(WCHAR*)malloc((len+1) * WCHAR.sizeof);
46         if (buf is null) return null;
47         scope (exit) free(buf);
48 
49         len = MultiByteToWideChar(CP_UTF8, 0, name.ptr,
50                                 cast(int)name.length, buf, len);
51         if (len == 0) return null;
52 
53         buf[len] = '\0';
54         return LoadLibraryW(buf);
55     }
56 }
57 
58 ///
59 void unloadLibrary(ref LibHandler lib)
60 {
61     version (Posix)
62     {
63         dlclose(&lib);
64         lib = null;
65     }
66     version (Windows)
67     {
68         FreeLibrary(lib);
69         lib = null;
70     }
71 }
72 
73 auto getSymbol(LibHandler lib, string name)
74 {
75     version (Posix)
76         return dlsym(lib, name.toStringz);
77     version (Windows)
78         return GetProcAddress(lib, name.toStringz);
79 }
80 
81 private enum __initDeclare = q{
82     import std.meta;
83     import std.typecons;
84     import std.traits;
85     import std.string;
86     //import core.sys.posix.dlfcn : dlsym;
87 
88     enum __dimmy;
89     alias __this = AliasSeq!(__traits(parent, __dimmy))[0];
90     enum __name = __traits(identifier, __this);
91 };
92 
93 private enum __callDeclare = q{
94     enum __pit = [ParameterIdentifierTuple!__this];
95     static if (!__pit.length) enum __params = "";
96     else enum __params = "%-(%s, %)".format(__pit);
97     enum __call = "__fnc(%s);".format(__params);
98     static if (is(ReturnType!__this == void))
99         enum __result = __call;
100     else
101         enum __result = "return " ~ __call;
102     mixin(__result);
103 };
104 
105 string rtLib()
106 {
107     return __initDeclare ~ q{
108     mixin("auto __fnc = %s;".format(apiFunctionPointerName(__name)));
109     } ~ __callDeclare;
110 }
111 
112 enum LoadApiSymbolsVerbose
113 {
114     none,
115     message,
116     assertion
117 }
118 
119 mixin template apiSymbols()
120 {
121     import std.meta;
122     import std.typecons;
123     import std.traits;
124 
125     enum __dimmy;
126 
127     template funcsByUDA(alias symbol, uda)
128     {
129         template impl(lst...)
130         {
131             static if (lst.length == 1)
132             {
133                 static if (is(typeof(__traits(getMember, symbol, lst[0])) == function))
134                 {
135                     alias ff = AliasSeq!(__traits(getMember, symbol, lst[0]))[0];
136                     static if (hasUDA!(ff, uda)) alias impl = AliasSeq!(ff);
137                     else alias impl = AliasSeq!();
138                 }
139                 else alias impl = AliasSeq!();
140             }
141             else alias impl = AliasSeq!(impl!(lst[0..$/2]), impl!(lst[$/2..$]));
142         }
143 
144         alias funcsByUDA = impl!(__traits(allMembers, symbol));
145     }
146 
147     alias apiFuncs = funcsByUDA!(__traits(parent, __dimmy), ApiUDA);
148 
149     void loadApiSymbols(LoadApiSymbolsVerbose verbose=LoadApiSymbolsVerbose.none)
150     {
151         import std.format : format;
152         import std.stdio : stderr;
153 
154         foreach (f; apiFuncs)
155         {
156             enum libname = getUDAs!(f, ApiUDA)[$-1].libname;
157             enum fname = __traits(identifier, f);
158             enum pname = apiFunctionPointerName(fname);
159             mixin(format!`%2$s = cast(typeof(%2$s))getSymbol(%3$s, "%1$s");`(fname, pname, libname));
160             if (mixin(pname ~ " is null"))
161             {
162                 auto errmsg = format!`can't find '%s' function`(fname);
163                 with (LoadApiSymbolsVerbose) final switch (verbose) 
164                 {
165                     case none: break;
166                     case message: stderr.writeln(errmsg); break;
167                     case assertion: assert(0, errmsg);
168                 }
169             }
170         }
171     }
172 
173     mixin funcPointers!apiFuncs;
174 }
175 
176 mixin template funcPointers(funcs...)
177 {
178     import std.string;
179     static if (funcs.length == 0) {}
180     else static if (funcs.length == 1)
181     {
182         alias __this = funcs[0];
183         mixin(`private __gshared extern(C) @nogc nothrow ReturnType!__this function(Parameters!__this) %s;`
184                 .format(apiFunctionPointerName(__traits(identifier, __this))));
185     }
186     else
187     {
188         mixin funcPointers!(funcs[0..$/2]);
189         mixin funcPointers!(funcs[$/2..$]);
190     }
191 }