diff --git a/cdata.lua b/cdata.lua index ac12575..f4013f7 100644 --- a/cdata.lua +++ b/cdata.lua @@ -2,6 +2,7 @@ -- Import local ffi = require 'ffi' +local bit = require 'bit' -- Constants @@ -14,18 +15,22 @@ local NUMBER_TO_HEX = { [12] = 'C', [13] = 'D', [14] = 'E', [15] = 'F', } -local function to_hex (str) +local function to_hex (val, nr_elements, element_size) local l = {} - for i = 1, #str do - local v = str:byte(i) - l[#l+1] = NUMBER_TO_HEX[math.floor(v / 16)] - l[#l+1] = NUMBER_TO_HEX[v % 16] + for i = 0, nr_elements - 1 do + local v = val[i] + l[#l+1] = bit.tohex(v, -2*element_size) l[#l+1] = ' ' end l[#l] = nil return table.concat(l, '') end +local function is_nice_unicode_string (str) + -- TODO... Maybe also look into a purely binary oriented representation. + return false +end + local function is_nice_ascii_string (str) for i = 1, #str do local byte = str:byte(i) @@ -35,24 +40,45 @@ local function is_nice_ascii_string (str) end local function get_type_and_size_of_singular ( ctype ) - local nr_elements = 1 + local nr_elements, layers = 1, 0 while true do local etype, elements = ctype:match('(.+)%[(%d*)%]$') if not elements then break end ctype, nr_elements = etype, nr_elements * elements + layers = layers + 1 end - return ctype, nr_elements + return ctype, nr_elements, layers end -------------------------------------------------------------------------------- local CDATA_REPR_MATCHER = 'cdata<(.+)>: (0x%w+)' -return function (value, options, depth, l) +local function format_cdata (value, options, depth, l, format_value) + local native_repr = tostring(value) local data_length = ffi.sizeof(value) local ctype, addr = native_repr:match(CDATA_REPR_MATCHER) + -- Is void pointer? + if ctype == 'void *' then + local address_pointing_at = tonumber(ffi.cast('int', value)) + l[#l+1] = 'void pointer to ' .. addr + return ; + end + + -- Is normal pointer? + if ctype:match('%*$') then + if type(value[0]) ~= 'cdata' then + -- Data presentable in Lua, refered to by pointers? + l[#l+1] = 'pointer to ' + return format_value(value[0], options, depth, l) + else + l[#l+1] = '* ' + return format_cdata(value[0], options, depth, l, format_value) + end + end + l[#l+1] = 'cdata {' --l[#l+1] = '\n\tnative = \'' .. native_repr .. '\',' l[#l+1] = '\n\ttype = ' .. ctype .. ',' @@ -63,21 +89,25 @@ return function (value, options, depth, l) l[#l+1] = '\n\tsize = ' .. data_length .. ',' -- Element size and type - local element_type, nr_elements = get_type_and_size_of_singular(ctype) + local element_type, nr_elements, nr_layers = get_type_and_size_of_singular(ctype) local element_size = data_length / nr_elements l[#l+1] = '\n\tnr_e = ' .. nr_elements .. ',' l[#l+1] = '\n\ttype_e = ' .. element_type .. ',' l[#l+1] = '\n\tsize_e = ' .. element_size .. ',' - -- - if is_nice_ascii_string(str) then - l[#l+1] = '\n\tstr = ' .. str .. ',' + -- If can be expressed as string, express it as string. + if is_nice_ascii_string(str) or is_nice_unicode_string(str) then + local string_or_unicode = is_nice_ascii_string(str) and 'ascii' or 'utf8 ' + l[#l+1] = '\n\t'..string_or_unicode..' = ' .. str .. ',' + end + -- + if nr_layers == 1 then + -- Only a single level of arrays + l[#l+1] = '\n\thex = ' .. to_hex(value, nr_elements, element_size) .. ',' end - l[#l+1] = '\n\tbin = ' .. to_hex(str) .. ',' - - - end l[#l+1] = '\n}' end + +return format_cdata diff --git a/test/test_pretty.lua b/test/test_pretty.lua index 87968bc..051eb75 100644 --- a/test/test_pretty.lua +++ b/test/test_pretty.lua @@ -19,15 +19,12 @@ local function format_test (t) if t.longterm then return end if t.adv_getlocal and not HAS_ADV_GETLOCAL then return end SUITE:addTest(t.expect, function () - local input_value = t.input - local input_options = t.options - local expected_result = t.expect - local actual_result = format(input_value, input_options) + local actual_result = format(t.input, t.options) if not t.approx or type(actual_result) ~= 'string' then - assert_equal(expected_result, actual_result) + assert_equal(t.expect, actual_result) else - if not actual_result:match(expected_result) then - error(ASSERT_ERROR_APPROX:format(expected_result, actual_result)) + if not actual_result:match(t.expect) then + error(ASSERT_ERROR_APPROX:format(t.expect, actual_result)) end end end) @@ -372,6 +369,10 @@ if type(jit) == 'table' then local ffi = require('ffi') ffi.cdef[[ + typedef struct foo { int a, b; } foo_t; + + void free(void *ptr); + void *malloc(size_t size); int poll(struct pollfd *fds, unsigned long nfds, int timeout); ]] @@ -389,6 +390,15 @@ if type(jit) == 'table' then } end + do + local list = ffi.new('int [17]') + for i = 0, 16 do list[i] = i end + format_test { + input = list, + expect = 'cdata<.+>: 0x%x+', + } + end + do local list = ffi.new('char [10]') for i = 0, 10-1 do list[i] = i + 65 end @@ -406,6 +416,31 @@ if type(jit) == 'table' then expect = 'cdata<.+>: 0x%x+', } end + + do + local p = ffi.gc(ffi.C.malloc(1), ffi.C.free) + format_test { + input = p, + expect = 'cdata<.+>: 0x%x+', + } + end + + SUITE:addTest('a_very_small_part_of_math', function () + local p = ffi.new('char[1]') + p[0] = 27 + local actual_result = format(p + 0, {}) + assert_equal('Derp', actual_result) + end) + + do + local p = ffi.new('foo_t[1]') + p[0].a = 27 + p[0].b = 27 + format_test { + input = p + 0, + expect = 'cdata<.+>: 0x%x+', + } + end end --------------------------------------------------------------------------------