Ocaml <-> C++ binding
[tatoo.git] / src / bindings / c++ / tatoo-engine.cc
1 #include <cassert>
2 #include <ctime>
3 #include <iostream>
4 #include <vector>
5
6 #include <caml/alloc.h>
7 #include <caml/callback.h>
8 #include <caml/memory.h>
9
10 #include <libxml/parser.h>
11 #include <libxml/tree.h>
12
13 #include "tatoo.h"
14
15 namespace ml {
16 value pack(void *ptr)
17 {
18     uintptr_t p = reinterpret_cast<uintptr_t>(ptr);
19     static_assert(sizeof(long) >= sizeof(uintptr_t), "We use long to store pointers");
20     assert(!(p&1));
21     return Val_long(p>>1);
22 }
23
24 void* unpack(value val) { return reinterpret_cast<void *>(Long_val(val) << 1); }
25
26 template<typename T>
27 T* unpack(value val) { return reinterpret_cast<T *>(unpack(val)); }
28 }
29
30 namespace xml {
31 xmlNode* node_first_child(xmlNode *node)
32 {
33     switch(node->type) {
34         case XML_ELEMENT_NODE:
35             if(node->properties) {
36                 return reinterpret_cast<xmlNode *>(node->properties);
37                 break;
38             }
39         case XML_ATTRIBUTE_NODE:
40         case XML_DOCUMENT_NODE:
41         case XML_TEXT_NODE:
42         case XML_PI_NODE:
43         case XML_COMMENT_NODE:
44             return node->children;
45         default:
46             assert(false && "Unknown node type");
47     }
48 }
49 xmlNode* node_next_sibling(xmlNode *node)
50 {
51     if(node->next)
52         return node->next;
53     if(node->type == XML_ATTRIBUTE_NODE)
54         return node->parent->children;
55     return NULL;
56 }
57 } // namespace xml
58
59 static value *init_document;
60 static value *xpath_compile;
61 static value *auto_evaluate;
62
63 typedef std::vector<xmlNode *> nodelist;
64
65 CAMLprim value node_getFirstChild(value node)
66
67     CAMLparam1(node);
68     CAMLreturn(ml::pack(xml::node_first_child(ml::unpack<xmlNode>(node))));
69 }
70
71 CAMLprim value node_getNextSibling(value node)
72 {       
73     CAMLparam1(node);
74     CAMLreturn(ml::pack(xml::node_next_sibling(ml::unpack<xmlNode>(node))));
75 }
76
77 CAMLprim value node_getNodeType(value node)
78
79     CAMLparam1(node);
80     CAMLreturn(Val_int(ml::unpack<xmlNode>(node)->type));
81 }
82
83 CAMLprim value node_getNodeName(value node)
84 {
85     CAMLparam1(node);
86     const xmlChar *name = ml::unpack<xmlNode>(node)->name;
87     static const char *empty = "";
88     CAMLreturn(caml_copy_string(name ? reinterpret_cast<const char *>(name) : empty));
89 }
90
91 CAMLprim value node_getPreorder(value node)
92
93     CAMLparam1(node);
94     CAMLreturn(Val_long(reinterpret_cast<uintptr_t>(ml::unpack<xmlNode>(node)->_private)));
95 }
96
97 CAMLprim value nodelist_getLength(value list)
98 {
99     CAMLparam1(list);
100     CAMLreturn(Val_long(ml::unpack<nodelist>(list)->size()));
101 }
102
103 CAMLprim value nodelist_item(value list, value idx)
104 {
105     CAMLparam2(list, idx);
106     CAMLreturn(ml::pack(ml::unpack<nodelist>(list)->operator[](Long_val(idx))));
107 }
108
109 CAMLprim value nodelist_new(value unit)
110 {
111     CAMLparam1(unit);
112     CAMLreturn(ml::pack(new nodelist)); // XXX: lifetime...
113 }
114
115 CAMLprim value nodelist_add(value list, value node)
116 {
117     CAMLparam2(list, node);
118     ml::unpack<nodelist>(list)->push_back(ml::unpack<xmlNode>(node));
119     CAMLreturn(list);
120 }
121
122 CAMLprim value getNull(value unit) { CAMLparam1(unit); CAMLreturn(ml::pack(NULL)); }
123
124 CAMLprim value dereference_object(value obj)
125 {
126     CAMLparam1(obj);
127     //jni::env().DeleteGlobalRef(reinterpret_cast<jobject>(obj));
128     CAMLreturn(Val_unit);
129 }
130
131 static uintptr_t decorate_(xmlNode *node, uintptr_t preorder)
132 {
133     if(not node) return preorder;
134
135     assert(!node->_private && "Non-null private data encountered");
136     node->_private = reinterpret_cast<void *>(preorder++);
137
138     preorder = decorate_(xml::node_first_child(node), preorder);
139     preorder = decorate_(xml::node_next_sibling(node), preorder);
140
141     return preorder;
142 }
143
144 static value decorate(xmlDoc *doc)
145 {
146     uintptr_t size = decorate_(reinterpret_cast<xmlNode *>(doc), 0);
147     return caml_callback2(*init_document, ml::pack(doc), Val_long(size));
148 }
149
150 template<typename Fn, typename... Args>
151 auto time(const char *msg, const Fn &fn, Args&&... args) -> decltype(fn(args...))
152 {
153     timespec start;
154     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start);
155     auto ret = fn(args...);
156     timespec end;
157     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
158     end.tv_nsec -= start.tv_nsec;
159     end.tv_sec -= start.tv_sec;
160     printf("\n---> %s: %.3f ms\n", msg, end.tv_sec * 1e3 + end.tv_nsec / 1e6);
161     return std::move(ret);
162 }
163
164 static void process(const char *file, const char *expr)
165 {
166     CAMLparam0();
167     CAMLlocal3(docval, aut, res);
168
169     xmlDoc *doc = time("parse", xmlReadFile, file, nullptr, 0);
170
171     docval = time("decorate", decorate, doc);
172
173     aut = time("compile", caml_callback, *xpath_compile, caml_copy_string(expr));
174
175     nodelist nl = { reinterpret_cast<xmlNode *>(doc) };
176     res = time("evaluate", caml_callback3, *auto_evaluate, aut, docval, ml::pack(&nl));
177     
178     nl = std::move(*ml::unpack<nodelist>(res));
179     printf("\n---> Number of results: %zd\n", nl.size());
180     /* for(auto node: nl)
181         printf("---> %s\n", node->name);*/
182
183     xmlFreeDoc(doc);
184     CAMLreturn0;
185 }
186
187 void usage(const char *program)
188 {
189     fprintf(stderr,
190             "Usage:\n"
191             "  %s <file> <query>\n", program);
192     exit(1);
193 }
194
195 int main(int argc, char *argv[])
196 {
197     LIBXML_TEST_VERSION;
198
199     if(argc != 3)
200         usage(argv[0]);
201
202     caml_startup(argv);
203
204     init_document = caml_named_value("init_document"); assert(init_document);
205     xpath_compile = caml_named_value("xpath_compile"); assert(xpath_compile);
206     auto_evaluate = caml_named_value("auto_evaluate"); assert(auto_evaluate);
207
208     process(argv[1], argv[2]);
209
210     xmlCleanupParser();
211     xmlMemoryDump();
212     return 0;
213 }