Repair Doxygen build infrastructure
[vpp.git] / vpp-api / lua / vpp-lapi.lua
1 --[[
2 /*
3  * Copyright (c) 2016 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 ]]
17
18 -- json decode/encode from https://gist.github.com/tylerneylon/59f4bcf316be525b30ab
19 -- licensed by the author tylerneylon into public domain. Thanks!
20
21 local json = {}
22
23 -- Internal functions.
24
25 local function kind_of(obj)
26   if type(obj) ~= 'table' then return type(obj) end
27   local i = 1
28   for _ in pairs(obj) do
29     if obj[i] ~= nil then i = i + 1 else return 'table' end
30   end
31   if i == 1 then return 'table' else return 'array' end
32 end
33
34 local function escape_str(s)
35   local in_char  = {'\\', '"', '/', '\b', '\f', '\n', '\r', '\t'}
36   local out_char = {'\\', '"', '/',  'b',  'f',  'n',  'r',  't'}
37   for i, c in ipairs(in_char) do
38     s = s:gsub(c, '\\' .. out_char[i])
39   end
40   return s
41 end
42
43 -- Returns pos, did_find; there are two cases:
44 -- 1. Delimiter found: pos = pos after leading space + delim; did_find = true.
45 -- 2. Delimiter not found: pos = pos after leading space;     did_find = false.
46 -- This throws an error if err_if_missing is true and the delim is not found.
47 local function skip_delim(str, pos, delim, err_if_missing)
48   pos = pos + #str:match('^%s*', pos)
49   if str:sub(pos, pos) ~= delim then
50     if err_if_missing then
51       error('Expected ' .. delim .. ' near position ' .. pos)
52     end
53     return pos, false
54   end
55   return pos + 1, true
56 end
57
58 -- Expects the given pos to be the first character after the opening quote.
59 -- Returns val, pos; the returned pos is after the closing quote character.
60 local function parse_str_val(str, pos, val)
61   val = val or ''
62   local early_end_error = 'End of input found while parsing string.'
63   if pos > #str then error(early_end_error) end
64   local c = str:sub(pos, pos)
65   if c == '"'  then return val, pos + 1 end
66   if c ~= '\\' then return parse_str_val(str, pos + 1, val .. c) end
67   -- We must have a \ character.
68   local esc_map = {b = '\b', f = '\f', n = '\n', r = '\r', t = '\t'}
69   local nextc = str:sub(pos + 1, pos + 1)
70   if not nextc then error(early_end_error) end
71   return parse_str_val(str, pos + 2, val .. (esc_map[nextc] or nextc))
72 end
73
74 -- Returns val, pos; the returned pos is after the number's final character.
75 local function parse_num_val(str, pos)
76   local num_str = str:match('^-?%d+%.?%d*[eE]?[+-]?%d*', pos)
77   local val = tonumber(num_str)
78   if not val then error('Error parsing number at position ' .. pos .. '.') end
79   return val, pos + #num_str
80 end
81
82
83 -- Public values and functions.
84
85 function json.stringify(obj, as_key)
86   local s = {}  -- We'll build the string as an array of strings to be concatenated.
87   local kind = kind_of(obj)  -- This is 'array' if it's an array or type(obj) otherwise.
88   if kind == 'array' then
89     if as_key then error('Can\'t encode array as key.') end
90     s[#s + 1] = '['
91     for i, val in ipairs(obj) do
92       if i > 1 then s[#s + 1] = ', ' end
93       s[#s + 1] = json.stringify(val)
94     end
95     s[#s + 1] = ']'
96   elseif kind == 'table' then
97     if as_key then error('Can\'t encode table as key.') end
98     s[#s + 1] = '{'
99     for k, v in pairs(obj) do
100       if #s > 1 then s[#s + 1] = ', ' end
101       s[#s + 1] = json.stringify(k, true)
102       s[#s + 1] = ':'
103       s[#s + 1] = json.stringify(v)
104     end
105     s[#s + 1] = '}'
106   elseif kind == 'string' then
107     return '"' .. escape_str(obj) .. '"'
108   elseif kind == 'number' then
109     if as_key then return '"' .. tostring(obj) .. '"' end
110     return tostring(obj)
111   elseif kind == 'boolean' then
112     return tostring(obj)
113   elseif kind == 'nil' then
114     return 'null'
115   else
116     error('Unjsonifiable type: ' .. kind .. '.')
117   end
118   return table.concat(s)
119 end
120
121 json.null = {}  -- This is a one-off table to represent the null value.
122
123 function json.parse(str, pos, end_delim)
124   pos = pos or 1
125   if pos > #str then error('Reached unexpected end of input.') end
126   local pos = pos + #str:match('^%s*', pos)  -- Skip whitespace.
127   local first = str:sub(pos, pos)
128   if first == '{' then  -- Parse an object.
129     local obj, key, delim_found = {}, true, true
130     pos = pos + 1
131     while true do
132       key, pos = json.parse(str, pos, '}')
133       if key == nil then return obj, pos end
134       if not delim_found then error('Comma missing between object items.') end
135       pos = skip_delim(str, pos, ':', true)  -- true -> error if missing.
136       obj[key], pos = json.parse(str, pos)
137       pos, delim_found = skip_delim(str, pos, ',')
138     end
139   elseif first == '[' then  -- Parse an array.
140     local arr, val, delim_found = {}, true, true
141     pos = pos + 1
142     while true do
143       val, pos = json.parse(str, pos, ']')
144       if val == nil then return arr, pos end
145       if not delim_found then error('Comma missing between array items.') end
146       arr[#arr + 1] = val
147       pos, delim_found = skip_delim(str, pos, ',')
148     end
149   elseif first == '"' then  -- Parse a string.
150     return parse_str_val(str, pos + 1)
151   elseif first == '-' or first:match('%d') then  -- Parse a number.
152     return parse_num_val(str, pos)
153   elseif first == end_delim then  -- End of an object or array.
154     return nil, pos + 1
155   else  -- Parse true, false, or null.
156     local literals = {['true'] = true, ['false'] = false, ['null'] = json.null}
157     for lit_str, lit_val in pairs(literals) do
158       local lit_end = pos + #lit_str - 1
159       if str:sub(pos, lit_end) == lit_str then return lit_val, lit_end + 1 end
160     end
161     local pos_info_str = 'position ' .. pos .. ': ' .. str:sub(pos, pos + 10)
162     error('Invalid json syntax starting at ' .. pos_info_str)
163   end
164 end
165
166
167 local vpp = {}
168
169 local ffi = require("ffi")
170
171 --[[
172
173 The basic type definitions. A bit of weird gymnastic with
174 unionization of the hton* and ntoh* functions results
175 is to make handling of signed and unsigned types a bit cleaner,
176 essentially building typecasting into a C union.
177
178 The vl_api_opaque_message_t is a synthetic type assumed to have
179 enough storage to hold the entire API message regardless of the type.
180 During the operation it is casted to the specific message struct types.
181
182 ]]
183
184
185 ffi.cdef([[
186
187 typedef uint8_t u8;
188 typedef int8_t i8;
189 typedef uint16_t u16;
190 typedef int16_t i16;
191 typedef uint32_t u32;
192 typedef int32_t i32;
193 typedef uint64_t u64;
194 typedef int64_t i64;
195 typedef double f64;
196 typedef float f32;
197
198 #pragma pack(1)
199 typedef union {
200   u16 u16;
201   i16 i16;
202 } lua_ui16t;
203
204 #pragma pack(1)
205 typedef union {
206   u32 u32;
207   i32 i32;
208 } lua_ui32t;
209
210 u16 ntohs(uint16_t hostshort);
211 u16 htons(uint16_t hostshort);
212 u32 htonl(uint32_t along);
213 u32 ntohl(uint32_t along);
214 void *memset(void *s, int c, size_t n);
215 void *memcpy(void *dest, void *src, size_t n);
216
217 #pragma pack(1)
218 typedef struct _vl_api_opaque_message {
219   u16 _vl_msg_id;
220   u8  data[65536];
221 } vl_api_opaque_message_t;
222 ]])
223
224
225 -- CRC-based version stuff
226
227 local crc32c_table = ffi.new('const uint32_t[256]',
228   { 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
229   0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
230   0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
231   0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
232   0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
233   0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
234   0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
235   0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
236   0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
237   0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
238   0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
239   0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
240   0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
241   0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
242   0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
243   0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
244   0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
245   0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
246   0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
247   0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
248   0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
249   0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
250   0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
251   0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
252   0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
253   0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
254   0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
255   0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
256   0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
257   0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
258   0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
259   0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
260   0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
261   0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
262   0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
263   0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
264   0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
265   0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
266   0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
267   0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
268   0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
269   0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
270   0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
271   0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
272   0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
273   0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
274   0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
275   0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
276   0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
277   0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
278   0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
279   0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
280   0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
281   0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
282   0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
283   0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
284   0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
285   0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
286   0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
287   0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
288   0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
289   0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
290   0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
291   0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }
292 );
293
294 local function CRC8(crc, d)
295   return bit.bxor(bit.rshift(crc, 8), crc32c_table[bit.band(0xff, bit.bxor(crc, d))])
296 end
297
298 local function CRC16(crc, d)
299   crc = CRC8(crc, bit.band(d, 0xFF))
300   d = bit.rshift(d, 8)
301   crc = CRC8(crc, bit.band(d, 0xFF))
302   return crc
303 end
304
305 local function string_crc(str, crc)
306   for i=1,#str do
307     -- print("S", i, string.byte(str, i), string.char(string.byte(str, i)))
308     crc = CRC8(crc, string.byte(str, i))
309   end
310   return crc
311 end
312
313 local tokens = {
314   { ["match"] =' ', ["act"]             = { }  },
315   { ["match"] ='\n', ["act"]             = { }  },
316   { ["match"] ="manual_endian", ["act"]  = { "NODE_MANUAL_ENDIAN", "MANUAL_ENDIAN",    276 } },
317   { ["match"] ="define", ["act"]         = { "NODE_DEFINE",        "DEFINE",           267 } },
318   { ["match"] ="dont_trace", ["act"]     = { "NODE_DONT_TRACE",    "DONT_TRACE",       279 } },
319   { ["match"] ="f64", ["act"]            = { "NODE_F64",           "PRIMTYPE",         string_crc } },
320   { ["match"] ="i16", ["act"]            = { "NODE_I16",           "PRIMTYPE",         string_crc } },
321   { ["match"] ="i32", ["act"]            = { "NODE_I32",           "PRIMTYPE",         string_crc } },
322   { ["match"] ="i64", ["act"]            = { "NODE_I64",           "PRIMTYPE",         string_crc } },
323   { ["match"] ="i8", ["act"]             = { "NODE_I8",            "PRIMTYPE",         string_crc } },
324   { ["match"] ="manual_print", ["act"]   = { "NODE_MANUAL_PRINT",  "MANUAL_PRINT",     275 } },
325   { ["match"] ="noversion", ["act"]      = { "NODE_NOVERSION",     "NOVERSION",        274 } },
326   { ["match"] ="packed", ["act"]         = { "NODE_PACKED",        "TPACKED",          266 } },
327   { ["match"] ="typeonly", ["act"]       = { "NODE_TYPEONLY",      "TYPEONLY",         278 } },
328   { ["match"] ="u16", ["act"]            = { "NODE_U16",           "PRIMTYPE",         string_crc } },
329   { ["match"] ="u32", ["act"]            = { "NODE_U32",           "PRIMTYPE",         string_crc } },
330   { ["match"] ="u64", ["act"]            = { "NODE_U64",           "PRIMTYPE",         string_crc } },
331   { ["match"] ="u8", ["act"]             = { "NODE_U8",            "PRIMTYPE",         string_crc } },
332   { ["match"] ="union", ["act"]          = { "NODE_UNION",         "UNION",            271 } },
333   { ["match"] ="uword", ["act"]          = { "NODE_UWORD",         "PRIMTYPE",         string_crc } },
334   { ["match"] ="%(", ["act"]             = { "NODE_LPAR",          "LPAR",             259 } },
335   { ["match"] ="%)", ["act"]             = { "NODE_RPAR",          "RPAR",             258 } },
336   { ["match"] =";", ["act"]              = { "NODE_SEMI",          "SEMI",             260 } },
337   { ["match"] ="%[", ["act"]             = { "NODE_LBRACK",        "LBRACK",           261 } },
338   { ["match"] ="%]", ["act"]             = { "NODE_RBRACK",        "RBRACK",           262 } },
339   { ["match"] ="%{", ["act"]             = { "NODE_LCURLY",        "LCURLY",           268 } },
340   { ["match"] ="%}", ["act"]             = { "NODE_RCURLY",        "RCURLY",           269 } },
341   { ["match"] ='%b""', ["act"]           = { "NODE_STRING",        "STRING",           string_crc } },
342   { ["match"] ='%b@@', ["act"]           = { "NODE_HELPER",        "HELPER_STRING",    string_crc } },
343   -- TODO: \ must be consumed
344   { ["match"] ='[_a-zA-Z][_a-zA-Z0-9]*',
345                        ["act"]           = { "NODE_NAME",          "NAME",             string_crc } },
346   { ["match"] ='[0-9]+', ["act"]         = { "NODE_NUMBER",        "NUMBER",           string_crc } },
347   { ["match"] ='#[^\n]+', ["act"]            = { "NODE_PRAGMA",        "PRAGMA",           nil } },
348 }
349
350
351 function vpp.crc_version_string(data)
352   local input_crc = 0
353   -- Get rid of comments
354   data = data:gsub("/%*.-%*/", "")
355   data = data:gsub("//[^\n]+", "")
356   -- print(data)
357   idx = 1
358   while (true) do
359     local matched = nil
360     for k, v in ipairs(tokens) do
361       if not matched then
362         local x, y, cap = string.find(data, v["match"], idx)
363         if x == idx then
364           matched = { ["node"] = v["act"], ["x"] = x, ["y"] = y, ["cap"] = cap, ["chars"] = string.sub(data, x, y)  }
365           -- print(k, v, x, y, cap, matched.chars, matched.node[0] )
366         end
367       end
368     end
369     if matched then
370       idx = idx + (matched.y - matched.x + 1)
371       if matched.node[1] then
372         local act = matched.node[3]
373         if type(act) == "function" then
374           input_crc = act(matched.chars, input_crc)
375         elseif type(act) == "number" then
376           input_crc = CRC16(input_crc, act)
377         end
378         -- print(vpp.dump(matched))
379       end
380     else
381       -- print("NOT MATCHED!")
382       local crc = CRC16(input_crc, 0xFFFFFFFF)
383       return string.sub(string.format("%x", crc), -8)
384     end
385   end
386 end
387
388
389 function vpp.dump(o)
390    if type(o) == 'table' then
391       local s = '{ '
392       for k,v in pairs(o) do
393          if type(k) ~= 'number' then k = '"'..k..'"' end
394          s = s .. '['..k..'] = ' .. vpp.dump(v) .. ','
395       end
396       return s .. '} '
397    else
398       return tostring(o)
399    end
400 end
401
402 function vpp.hex_dump(buf)
403   local ret = {}
404   for i=1,math.ceil(#buf/16) * 16 do
405     if (i-1) % 16 == 0 then table.insert(ret, string.format('%08X  ', i-1)) end
406     table.insert(ret, ( i > #buf and '   ' or string.format('%02X ', buf:byte(i)) ))
407     if i %  8 == 0 then table.insert(ret, ' ') end
408     if i % 16 == 0 then table.insert(ret, buf:sub(i-16+1, i):gsub('%c','.')..'\n' ) end
409   end
410   return table.concat(ret)
411 end
412
413
414 function vpp.c_str(text_in)
415   local text = text_in -- \000 will be helpfully added by ffi.copy
416   local c_str = ffi.new("char[?]", #text+1)
417   ffi.copy(c_str, text)
418   return c_str
419 end
420
421
422 function vpp.init(vpp, args)
423   local pneum_api = args.pneum_api or [[
424  int cough_pneum_attach(char *pneum_path, char *cough_path);
425  int pneum_connect(char *name, char *chroot_prefix, void *cb);
426  int pneum_disconnect(void);
427  int pneum_read(char **data, int *l);
428  int pneum_write(char *data, int len);
429  void pneum_free(char *data);
430  uint32_t pneum_get_msg_index(unsigned char * name);
431 ]]
432
433   vpp.pneum_path = args.pneum_path
434   ffi.cdef(pneum_api)
435   local init_res = 0
436   vpp.pneum = ffi.load(vpp.pneum_path)
437   if (init_res < 0) then
438     return nil
439   end
440
441   vpp.next_msg_num = 1
442   vpp.msg_name_to_number = {}
443   vpp.msg_name_to_fields = {}
444   vpp.msg_number_to_name = {}
445   vpp.msg_number_to_type = {}
446   vpp.msg_number_to_pointer_type = {}
447   vpp.msg_name_to_crc = {}
448   vpp.c_type_to_fields = {}
449   vpp.events = {}
450   vpp.plugin_version = {}
451   vpp.is_connected = false
452
453
454   vpp.t_lua2c = {}
455   vpp.t_c2lua = {}
456   vpp.t_lua2c["u8"] = function(c_type, src, dst_c_ptr)
457     if type(src) == "string" then
458       -- ffi.copy adds a zero byte at the end. Grrr.
459       -- ffi.copy(dst_c_ptr, src)
460       ffi.C.memcpy(dst_c_ptr, vpp.c_str(src), #src)
461       return(#src)
462     elseif type(src) == "table" then
463       for i,v in ipairs(src) do
464         ffi.cast("u8 *", dst_c_ptr)[i-1] = v
465       end
466       return(#src)
467     else
468       return 1, src -- ffi.cast("u8", src)
469     end
470   end
471   vpp.t_c2lua["u8"] = function(c_type, src_ptr, src_len)
472     if src_len then
473       return ffi.string(src_ptr, src_len)
474     else
475       return (tonumber(src_ptr))
476     end
477   end
478
479   vpp.t_lua2c["u16"] = function(c_type, src, dst_c_ptr)
480     if type(src) == "table" then
481       for i,v in ipairs(src) do
482         ffi.cast("u16 *", dst_c_ptr)[i-1] = ffi.C.htons(v)
483       end
484       return(2 * #src)
485     else
486       return 2, (ffi.C.htons(src))
487     end
488   end
489   vpp.t_c2lua["u16"] = function(c_type, src_ptr, src_len)
490     if src_len then
491       local out = {}
492       for i = 0,src_len-1 do
493         out[i+1] = tonumber(ffi.C.ntohs(src_ptr[i]))
494       end
495       return out
496     else
497       return (tonumber(ffi.C.ntohs(src_ptr)))
498     end
499   end
500
501   vpp.t_lua2c["u32"] = function(c_type, src, dst_c_ptr)
502     if type(src) == "table" then
503       for i,v in ipairs(src) do
504         ffi.cast("u32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
505       end
506       return(4 * #src)
507     else
508       return 4, (ffi.C.htonl(src))
509     end
510   end
511   vpp.t_c2lua["u32"] = function(c_type, src_ptr, src_len)
512     if src_len then
513       local out = {}
514       for i = 0,src_len-1 do
515         out[i+1] = tonumber(ffi.C.ntohl(src_ptr[i]))
516       end
517       return out
518     else
519       return (tonumber(ffi.C.ntohl(src_ptr)))
520     end
521   end
522   vpp.t_lua2c["i32"] = function(c_type, src, dst_c_ptr)
523     if type(src) == "table" then
524       for i,v in ipairs(src) do
525         ffi.cast("i32 *", dst_c_ptr)[i-1] = ffi.C.htonl(v)
526       end
527       return(4 * #src)
528     else
529       return 4, (ffi.C.htonl(src))
530     end
531   end
532   vpp.t_c2lua["i32"] = function(c_type, src_ptr, src_len)
533     local ntohl = function(src)
534       local u32val = ffi.cast("u32", src)
535       local ntohlval = (ffi.C.ntohl(u32val))
536       local out = tonumber(ffi.cast("i32", ntohlval + 0LL))
537       return out
538     end
539     if src_len then
540       local out = {}
541       for i = 0,src_len-1 do
542         out[i+1] = tonumber(ntohl(src_ptr[i]))
543       end
544     else
545       return (tonumber(ntohl(src_ptr)))
546     end
547   end
548
549   vpp.t_lua2c["u64"] = function(c_type, src, dst_c_ptr)
550     if type(src) == "table" then
551       for i,v in ipairs(src) do
552         ffi.cast("u64 *", dst_c_ptr)[i-1] = v --- FIXME ENDIAN
553       end
554       return(8 * #src)
555     else
556       return 8, ffi.cast("u64", src) --- FIXME ENDIAN
557     end
558   end
559   vpp.t_c2lua["u64"] = function(c_type, src_ptr, src_len)
560     if src_len then
561       local out = {}
562       for i = 0,src_len-1 do
563         out[i+1] = tonumber(src_ptr[i]) -- FIXME ENDIAN
564       end
565       return out
566     else
567       return (tonumber(src_ptr)) --FIXME ENDIAN
568     end
569   end
570
571
572
573
574   vpp.t_lua2c["__MSG__"] = function(c_type, src, dst_c_ptr)
575     local dst = ffi.cast(c_type .. " *", dst_c_ptr)
576     local additional_len = 0
577     local fields_info = vpp.c_type_to_fields[c_type]
578     -- print("__MSG__ type: " .. tostring(c_type))
579     ffi.C.memset(dst_c_ptr, 0, ffi.sizeof(dst[0]))
580     -- print(vpp.dump(fields_info))
581     -- print(vpp.dump(src))
582     for k,v in pairs(src) do
583       local field = fields_info[k]
584       if not field then
585         print("ERROR: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is unknown")
586       end
587       local lua2c = vpp.t_lua2c[field.c_type]
588       -- print("__MSG__ field " .. tostring(k) .. " : " .. vpp.dump(field))
589       -- if the field is not an array type, try to coerce the argument to a number
590       if not field.array and type(v) == "string" then
591         v = tonumber(v)
592       end
593       if not lua2c then
594         print("__MSG__ " .. tostring(c_type) .. " t_lua2c: can not store field " .. field.name ..
595               " type " .. field.c_type .. " dst " .. tostring(dst[k]))
596         return 0
597       end
598       local len = 0
599       local val = nil
600       if field.array and (type(v) == "table") then
601         -- print("NTFY: field " .. tostring(k) .. " in message " .. tostring(c_type) .. " is an array")
602         for field_i, field_v in ipairs(v) do
603           -- print("NTFY: setting member#" .. tostring(field_i) .. " to value " .. vpp.dump(field_v))
604           local field_len, field_val = lua2c(field.c_type, field_v, dst[k][field_i-1])
605           len = len + field_len
606         end
607       else
608         len, val = lua2c(field.c_type, v, dst[k])
609       end
610       if not field.array then
611         dst[k] = val
612       else
613         if 0 == field.array then
614           additional_len = additional_len + len
615           -- print("Adding " .. tostring(len) .. " bytes due to field " .. tostring(field.name))
616           -- If there is a variable storing the length
617           -- and the input table does not set it, do magic
618           if field.array_size and not src[field.array_size] then
619             local size_field = fields_info[field.array_size]
620             if size_field then
621               dst[field.array_size] = vpp.t_c2lua[size_field.c_type](size_field.c_type, len)
622             end
623           end
624         end
625       end
626       -- print("Full message:\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), 64)))
627     end
628     return (ffi.sizeof(dst[0])+additional_len)
629   end
630
631   vpp.t_c2lua["__MSG__"] = function(c_type, src_ptr, src_len)
632     local out = {}
633     local reply_typed_ptr = ffi.cast(c_type .. " *", src_ptr)
634     local field_desc = vpp.c_type_to_fields[c_type]
635     if src_len then
636       for i = 0,src_len-1 do
637         out[i+1] = vpp.t_c2lua[c_type](c_type, src_ptr[i])
638       end
639       return out
640     end
641
642     for k, v in pairs(field_desc) do
643       local v_c2lua = vpp.t_c2lua[v.c_type]
644       if v_c2lua then
645         local len = v.array
646         -- print(dump(v))
647         if len then
648           local len_field_name = k .. "_length"
649           local len_field = field_desc[len_field_name]
650           if (len_field) then
651             local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[len_field_name])
652             out[k] =  v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
653           elseif len == 0 then
654             -- check if len = 0, then must be a field which contains the size
655             len_field =  field_desc[v.array_size]
656             local real_len = vpp.t_c2lua[len_field.c_type](len_field.c_type, reply_typed_ptr[v.array_size])
657             -- print("REAL length: " .. vpp.dump(v) .. " : " .. tostring(real_len))
658             out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], real_len)
659           else
660             -- alas, just stuff the entire array
661             out[k] = v_c2lua(v.c_type, reply_typed_ptr[k], len)
662           end
663         else
664           out[k] =  v_c2lua(v.c_type, reply_typed_ptr[k])
665         end
666       else
667         out[k] = "<no accessor function for type " .. tostring(v.c_type) .. ">"
668       end
669       -- print(k, out[k])
670     end
671     return out
672   end
673
674   return vpp
675 end
676
677 function vpp.resolve_message_number(msgname)
678   local name = msgname .. "_" .. vpp.msg_name_to_crc[msgname]
679   local idx = vpp.pneum.pneum_get_msg_index(vpp.c_str(name))
680   if vpp.debug_dump then
681     print("Index for " .. tostring(name) .. " is " .. tostring(idx))
682   end
683   vpp.msg_name_to_number[msgname] = idx
684   vpp.msg_number_to_name[idx] = msgname
685   vpp.msg_number_to_type[idx] = "vl_api_" .. msgname .. "_t"
686   vpp.msg_number_to_pointer_type[idx] = vpp.msg_number_to_type[idx] .. " *"
687   ffi.cdef("\n\n enum { vl_msg_" .. msgname .. " = " .. idx .. " };\n\n")
688 end
689
690 function vpp.connect(vpp, client_name)
691     local name = "lua_client"
692     if client_name then
693       name = client_name
694     end
695     local ret = vpp.pneum.pneum_connect(vpp.c_str(client_name), nil, nil)
696     if tonumber(ret) == 0 then
697       vpp.is_connected = true
698     end
699     for k, v in pairs(vpp.msg_name_to_number) do
700       vpp.resolve_message_number(k)
701     end
702   end
703
704 function vpp.disconnect(vpp)
705     vpp.pneum.pneum_disconnect()
706   end
707
708 function vpp.json_api(vpp, path, plugin_name)
709     -- print("Consuming the VPP api from "..path)
710     local ffii = {}
711     local f = io.open(path, "r")
712     if not f then
713       print("Could not open " .. path)
714       return nil
715     end
716     local data = f:read("*all")
717     local json = json.parse(data)
718     if not (json.types or json.messages) then
719       print("Can not parse " .. path)
720       return nil
721     end
722
723     local all_types = {}
724
725     for i, v in ipairs(json.types) do
726       table.insert(all_types, { typeonly = 1, desc = v })
727     end
728     for i, v in ipairs(json.messages) do
729       table.insert(all_types, { typeonly = 0, desc = v })
730     end
731     for i, v in ipairs(all_types) do
732       local typeonly = v.typeonly
733       local name = v.desc[1]
734       local c_type = "vl_api_" .. name .. "_t"
735
736       local fields = {}
737       -- vpp.msg_name_to_fields[name] = fields
738       -- print("CTYPE " .. c_type)
739       vpp.c_type_to_fields[c_type] = fields
740       vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
741       vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
742
743       local cdef = { "\n\n#pragma pack(1)\ntypedef struct _vl_api_", name, " {\n" }
744       for ii, vv in ipairs(v.desc) do
745         if type(vv) == "table" then
746           if vv.crc then
747             vpp.msg_name_to_crc[name] = string.sub(vv.crc, 3) -- strip the leading 0x
748           else
749             local fieldtype = vv[1]
750             local fieldname = vv[2]
751             local fieldcount = vv[3]
752             local fieldcountvar = vv[4]
753             local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
754             if fieldcount then
755               table.insert(cdef, "  " .. fieldtype .. " " .. fieldname .. "[" .. fieldcount .. "];\n")
756               if fieldtype == "u8" then
757                 -- any array of bytes is treated as a string
758               elseif vpp.t_lua2c[fieldtype] then
759                 -- print("Array of " .. fieldtype .. " is ok!")
760               else
761                 print("Unknown array type: ", name,  " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
762               end
763             else
764               table.insert(cdef, "  " .. fieldtype .. " " .. fieldname .. ";\n")
765             end
766             fields[fieldname] = fieldrec
767           end
768         end
769       end
770
771       table.insert(cdef, "} vl_api_" .. name .. "_t;")
772       table.insert(ffii, table.concat(cdef))
773
774       if typeonly == 0 then
775         -- we will want to resolve this later
776         if vpp.debug_dump then
777           print("Remember to resolve " .. name)
778         end
779         vpp.msg_name_to_number[name] = -1
780         if vpp.is_connected then
781           vpp.resolve_message_number(name)
782         end
783       end
784
785     end
786     local cdef_full = table.concat(ffii)
787     ffi.cdef(cdef_full)
788 end
789
790 function vpp.consume_api(vpp, path, plugin_name)
791     -- print("Consuming the VPP api from "..path)
792     local ffii = {}
793     local f = io.open(path, "r")
794     if not f then
795       print("Could not open " .. path)
796       return nil
797     end
798     local data = f:read("*all")
799     -- Remove all C comments
800     data = data:gsub("/%*.-%*/", "")
801     if vpp.is_connected and not plugin_name then
802       print(path .. ": must specify plugin name!")
803       return
804     end
805     if plugin_name then
806       vpp.plugin_version[plugin_name] = vpp.crc_version_string(data)
807       local full_plugin_name = plugin_name .. "_" .. vpp.plugin_version[plugin_name]
808       local reply = vpp:api_call("get_first_msg_id", { name = full_plugin_name } )
809       vpp.next_msg_num = tonumber(reply[1].first_msg_id)
810       print("Plugin " .. full_plugin_name .. " first message is " .. tostring(vpp.next_msg_num))
811     end
812     -- print ("data len: ", #data)
813     data = data:gsub("\n(.-)(%S+)%s*{([^}]*)}", function (preamble, name, members)
814       local _, typeonly = preamble:gsub("typeonly", "")
815       local maybe_msg_id_field = { [0] = "u16 _vl_msg_id;", "" }
816       local onedef = "\n\n#pragma pack(1)\ntypedef struct _vl_api_"..name.. " {\n" ..
817            -- "   u16 _vl_msg_id;" ..
818            maybe_msg_id_field[typeonly] ..
819            members:gsub("%[[a-zA-Z_]+]", "[0]") ..
820            "} vl_api_" .. name .. "_t;"
821
822       local c_type = "vl_api_" .. name .. "_t"
823
824       local fields = {}
825       -- vpp.msg_name_to_fields[name] = fields
826       -- print("CTYPE " .. c_type)
827       vpp.c_type_to_fields[c_type] = fields
828       vpp.t_lua2c[c_type] = vpp.t_lua2c["__MSG__"]
829       vpp.t_c2lua[c_type] = vpp.t_c2lua["__MSG__"]
830       local mirec = { name = "_vl_msg_id", c_type = "u16", array = nil, array_size = nil }
831       if typeonly == 0 then
832         fields[mirec.name] = mirec
833       end
834
835       -- populate the field reflection table for the message
836       -- sets the various type information as well as the accessors for lua<->C conversion
837       members:gsub("(%S+)%s+(%S+);", function (fieldtype, fieldname)
838           local fieldcount = nil
839           local fieldcountvar = nil
840           -- data = data:gsub("%[[a-zA-Z_]+]", "[0]")
841           fieldname = fieldname:gsub("(%b[])", function(cnt)
842               fieldcount = tonumber(cnt:sub(2, -2));
843               if not fieldcount then
844                 fieldcount = 0
845                 fieldcountvar = cnt:sub(2, -2)
846               end
847               return ""
848             end)
849           local fieldrec = { name = fieldname, c_type = fieldtype, array = fieldcount, array_size = fieldcountvar }
850           if fieldcount then
851             if fieldtype == "u8" then
852               -- any array of bytes is treated as a string
853             elseif vpp.t_lua2c[fieldtype] then
854               -- print("Array of " .. fieldtype .. " is ok!")
855             else
856               print("Unknown array type: ", name,  " : " , fieldname, " : ", fieldtype, ":", fieldcount, ":", fieldcountvar)
857             end
858           end
859           fields[fieldname] = fieldrec
860         end)
861
862       -- print(dump(fields))
863
864       if typeonly == 0 then
865         local this_message_number = vpp.next_msg_num
866         vpp.next_msg_num = vpp.next_msg_num + 1
867         vpp.msg_name_to_number[name] = this_message_number
868         vpp.msg_number_to_name[this_message_number] = name
869         vpp.msg_number_to_type[this_message_number] = "vl_api_" .. name .. "_t"
870         vpp.msg_number_to_pointer_type[this_message_number] = vpp.msg_number_to_type[this_message_number] .. " *"
871         onedef = onedef .. "\n\n enum { vl_msg_" .. name .. " = " .. this_message_number .. " };\n\n"
872       end
873       table.insert(ffii, onedef);
874       return "";
875       end)
876     local cdef = table.concat(ffii)
877     -- print(cdef)
878     ffi.cdef(cdef)
879   end
880
881
882 function vpp.lua2c(vpp, c_type, src, dst_c_ptr)
883   -- returns the number of bytes written to memory pointed by dst
884   local lua2c = vpp.t_lua2c[c_type]
885   if lua2c then
886     return(lua2c(c_type, src, dst_c_ptr))
887   else
888     print("vpp.lua2c: do not know how to store type " .. tostring(c_type))
889     local x = "a" .. nil
890     return 0
891   end
892 end
893
894 function vpp.c2lua(vpp, c_type, src_ptr, src_len)
895   -- returns the lua data structure
896   local c2lua = vpp.t_c2lua[c_type]
897   if c2lua then
898     return(c2lua(c_type, src_ptr, src_len))
899   else
900     print("vpp.c2lua: do not know how to load type " .. c_type)
901     return nil
902   end
903 end
904
905 local req_store_cache = ffi.new("vl_api_opaque_message_t[1]")
906
907 function vpp.api_write(vpp, api_name, req_table)
908     local msg_num = vpp.msg_name_to_number[api_name]
909     if not msg_num then
910       print ("API call "..api_name.." is not known")
911       return nil
912     end
913
914     if not req_table then
915       req_table = {}
916     end
917     req_table._vl_msg_id = msg_num
918
919     local packed_len = vpp:lua2c(vpp.msg_number_to_type[msg_num], req_table, req_store_cache)
920     if vpp.debug_dump then
921       print("Write Message length: " .. tostring(packed_len) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', req_store_cache), packed_len)))
922     end
923
924     res = vpp.pneum.pneum_write(ffi.cast('void *', req_store_cache), packed_len)
925     return res
926   end
927
928 local rep_store_cache = ffi.new("vl_api_opaque_message_t *[1]")
929 local rep_len_cache = ffi.new("int[1]")
930
931 function vpp.api_read(vpp)
932     local rep_type = "vl_api_opaque_message_t"
933     local rep = rep_store_cache
934     local replen = rep_len_cache
935     res = vpp.pneum.pneum_read(ffi.cast("void *", rep), replen)
936     if vpp.debug_dump then
937       print("Read Message length: " .. tostring(replen[0]) .. "\n" .. vpp.hex_dump(ffi.string(ffi.cast('void *', rep[0]), replen[0])))
938     end
939
940     local reply_msg_num = ffi.C.ntohs(rep[0]._vl_msg_id)
941     local reply_msg_name = vpp.msg_number_to_name[reply_msg_num]
942
943     local reply_typed_ptr = ffi.cast(vpp.msg_number_to_pointer_type[reply_msg_num], rep[0])
944     local out = vpp:c2lua(vpp.msg_number_to_type[reply_msg_num], rep[0], nil, replen[0])
945     if type(out) == "table" then
946       out["luaapi_message_name"] = reply_msg_name
947     end
948
949     vpp.pneum.pneum_free(ffi.cast('void *',rep[0]))
950
951     return reply_msg_name, out
952   end
953
954 function vpp.api_call(vpp, api_name, req_table, options_in)
955     local msg_num = vpp.msg_name_to_number[api_name]
956     local end_message_name = api_name .."_reply"
957     local replies = {}
958     local cstruct = ""
959     local options = options_in or {}
960     if msg_num then
961       if vpp.debug_dump then
962         print("Message #" .. tostring(msg_num) .. " for name " .. tostring(api_name))
963       end
964       vpp:api_write(api_name, req_table)
965       if not vpp.msg_name_to_number[end_message_name] or options.force_ping then
966         end_message_name = "control_ping_reply"
967         vpp:api_write("control_ping")
968       end
969       repeat
970         reply_message_name, reply = vpp:api_read()
971         if reply and not reply.context then
972           -- there may be async events inbetween
973           table.insert(vpp.events, reply)
974         else
975           if reply_message_name ~= "control_ping_reply" then
976             -- do not insert the control ping encapsulation
977             table.insert(replies, reply)
978           end
979         end
980         -- print(reply)
981       until reply_message_name == end_message_name
982     else
983       print(api_name .. " is an unknown API call")
984       return nil
985     end
986     return replies
987   end
988
989 return vpp