if exists('g:loaded_msgpack_autoload') finish endif let g:loaded_msgpack_autoload = 1 "" " Check that given value is an integer. Respects |msgpack-special-dict|. function msgpack#is_int(v) abort return type(a:v) == type(0) || ( \type(a:v) == type({}) && get(a:v, '_TYPE') is# v:msgpack_types.integer) endfunction "" " Check that given value is an unsigned integer. Respects " |msgpack-special-dict|. function msgpack#is_uint(v) abort return msgpack#is_int(a:v) && (type(a:v) == type(0) \? a:v >= 0 \: a:v._VAL[0] > 0) endfunction "" " True if s:msgpack_init_python() function was already run. let s:msgpack_python_initialized = 0 "" " Cached return of s:msgpack_init_python() used when " s:msgpack_python_initialized is true. let s:msgpack_python_type = 0 "" " Create Python functions that are necessary for work. Also defines functions " s:msgpack_dict_strftime(format, timestamp) and s:msgpack_dict_strptime(format, " string). " " @return Zero in case no Python is available, empty string if Python-2 is " available and string `"3"` if Python-3 is available. function s:msgpack_init_python() abort if s:msgpack_python_initialized return s:msgpack_python_type endif let s:msgpack_python_initialized = 1 for suf in (has('win32') ? ['3'] : ['', '3']) try execute 'python' . suf \. "\n" \. "def shada_dict_strftime():\n" \. " import datetime\n" \. " import vim\n" \. " fmt = vim.eval('a:format')\n" \. " timestamp = vim.eval('a:timestamp')\n" \. " timestamp = [int(v) for v in timestamp['_VAL']]\n" \. " timestamp = timestamp[0] * (timestamp[1] << 62\n" \. " | timestamp[2] << 31\n" \. " | timestamp[3])\n" \. " time = datetime.datetime.fromtimestamp(timestamp)\n" \. " return time.strftime(fmt)\n" \. "def shada_dict_strptime():\n" \. " import calendar\n" \. " import datetime\n" \. " import vim\n" \. " fmt = vim.eval('a:format')\n" \. " timestr = vim.eval('a:string')\n" \. " timestamp = datetime.datetime.strptime(timestr, fmt)\n" \. " try:\n" \. " timestamp = int(timestamp.timestamp())\n" \. " except:\n" \. " try:\n" \. " timestamp = int(timestamp.strftime('%s'))\n" \. " except:\n" \. " timestamp = calendar.timegm(timestamp.utctimetuple())\n" \. " if timestamp > 2 ** 31:\n" \. " tsabs = abs(timestamp)\n" \. " return ('{\"_TYPE\": v:msgpack_types.integer,'\n" \. " + '\"_VAL\": [{sign},{v1},{v2},{v3}]}').format(\n" \. " sign=(1 if timestamp >= 0 else -1),\n" \. " v1=((tsabs >> 62) & 0x3),\n" \. " v2=((tsabs >> 31) & (2 ** 31 - 1)),\n" \. " v3=(tsabs & (2 ** 31 - 1)))\n" \. " else:\n" \. " return str(timestamp)\n" execute "function s:msgpack_dict_strftime(format, timestamp) abort\n" \. " return py" . suf . "eval('shada_dict_strftime()')\n" \. "endfunction\n" \. "function s:msgpack_dict_strptime(format, string)\n" \. " return eval(py" . suf . "eval('shada_dict_strptime()'))\n" \. "endfunction\n" let s:msgpack_python_type = suf return suf catch continue endtry endfor "" " strftime() function for |msgpack-special-dict| values. " " @param[in] format String according to which time should be formatted. " @param[in] timestamp Timestamp (seconds since epoch) to format. " " @return Formatted timestamp. " " @warning Without +python or +python3 this function does not work correctly. " The Vimscript code contains “reference” implementation which does " not really work because of precision loss. function s:msgpack_dict_strftime(format, timestamp) return msgpack#strftime(a:format, +msgpack#int_dict_to_str(a:timestamp)) endfunction "" " Function that parses given string according to given format. " " @param[in] format String according to which string was formatted. " @param[in] string Time formatted according to format. " " @return Timestamp. " " @warning Without +python or +python3 this function is able to work only with " 31-bit (32-bit signed) timestamps that have format " `%Y-%m-%dT%H:%M:%S`. function s:msgpack_dict_strptime(format, string) let fmt = '%Y-%m-%dT%H:%M:%S' if a:format isnot# fmt throw 'notimplemented-format:Only ' . fmt . ' format is supported' endif let match = matchlist(a:string, \'\v\C^(\d+)\-(\d+)\-(\d+)T(\d+)\:(\d+)\:(\d+)$') if empty(match) throw 'invalid-string:Given string does not match format ' . a:format endif call map(match, 'str2nr(v:val, 10)') let [year, month, day, hour, minute, second] = match[1:6] " Bisection start and end: " " Start: 365 days in year, 28 days in month, -12 hours tz offset. let bisect_ts_start = (((((year - 1970) * 365 \+ (month - 1) * 28 \+ (day - 1)) * 24 \+ hour - 12) * 60 \+ minute) * 60 \+ second) if bisect_ts_start < 0 let bisect_ts_start = 0 endif let start_string = strftime(fmt, bisect_ts_start) if start_string is# a:string return bisect_ts_start endif " End: 366 days in year, 31 day in month, +14 hours tz offset. let bisect_ts_end = (((((year - 1970) * 366 \+ (month - 1) * 31 \+ (day - 1)) * 24 \+ hour + 14) * 60 \+ minute) * 60 \+ second) let end_string = strftime(fmt, bisect_ts_end) if end_string is# a:string return bisect_ts_end endif if start_string ># end_string throw 'internal-start-gt:Internal error: start > end' endif if start_string is# end_string throw printf('internal-start-eq:Internal error: ' \. 'start(%u)==end(%u), but start(%s)!=string(%s)', \bisect_ts_start, bisect_ts_end, \string(start_string), string(a:string)) endif if start_string ># a:string throw 'internal-start-string:Internal error: start > string' endif if end_string <# a:string throw 'internal-end-string:Internal error: end < string' endif while 1 let bisect_ts_middle = (bisect_ts_start/2) + (bisect_ts_end/2) let middle_string = strftime(fmt, bisect_ts_middle) if a:string is# middle_string return bisect_ts_middle elseif a:string ># middle_string if bisect_ts_middle == bisect_ts_start let bisect_ts_start += 1 else let bisect_ts_start = bisect_ts_middle endif else if bisect_ts_middle == bisect_ts_end let bisect_ts_end -= 1 else let bisect_ts_end = bisect_ts_middle endif endif if bisect_ts_start >= bisect_ts_end throw 'not-found:Unable to find timestamp' endif endwhile endfunction return 0 endfunction "" " Wrapper for strftime() that respects |msgpack-special-dict|. May actually use " non-standard strftime() implementations for |msgpack-special-dict| values. " " @param[in] format Format string. " @param[in] timestamp Formatted timestamp. function msgpack#strftime(format, timestamp) abort if type(a:timestamp) == type({}) call s:msgpack_init_python() return s:msgpack_dict_strftime(a:format, a:timestamp) else return strftime(a:format, a:timestamp) endif endfunction "" " Parse string according to the format. " " Requires +python available. If it is not then only supported format is " `%Y-%m-%dT%H:%M:%S` because this is the format used by ShaDa plugin. Also in " this case bisection will be used (timestamps tried with strftime() up until " result matches the string) and only 31-bit (signed 32-bit: with negative " timestamps being useless this leaves 31 bits) timestamps will be supported. " " @param[in] format Time format. " @param[in] string Parsed time string. Must match given format. " " @return Timestamp. Possibly as |msgpack-special-dict|. function msgpack#strptime(format, string) abort call s:msgpack_init_python() return s:msgpack_dict_strptime(a:format, a:string) endfunction let s:MSGPACK_HIGHEST_BIT = 1 let s:MSGPACK_HIGHEST_BIT_NR = 0 while s:MSGPACK_HIGHEST_BIT * 2 > 0 let s:MSGPACK_HIGHEST_BIT = s:MSGPACK_HIGHEST_BIT * 2 let s:MSGPACK_HIGHEST_BIT_NR += 1 endwhile "" " Shift given number by given amount of bits function s:shift(n, s) abort if a:s == 0 return a:n elseif a:s < 0 let ret = a:n for _ in range(-a:s) let ret = ret / 2 endfor return ret else let ret = a:n for i in range(a:s) let new_ret = ret * 2 if new_ret < ret " Overflow: remove highest bit let ret = xor(s:MSGPACK_HIGHEST_BIT, ret) * 2 endif let ret = new_ret endfor return ret endif endfunction let s:msgpack_mask_cache = { \s:MSGPACK_HIGHEST_BIT_NR : s:MSGPACK_HIGHEST_BIT - 1} "" " Apply a mask where first m bits are ones and other are zeroes to a given " number function s:mask1(n, m) abort if a:m > s:MSGPACK_HIGHEST_BIT_NR + 1 let m = s:MSGPACK_HIGHEST_BIT_NR + 1 else let m = a:m endif if !has_key(s:msgpack_mask_cache, m) let p = 0 for _ in range(m) let p = p * 2 + 1 endfor let s:msgpack_mask_cache[m] = p endif return and(a:n, s:msgpack_mask_cache[m]) endfunction "" " Convert |msgpack-special-dict| that represents integer value to a string. Uses " hexadecimal representation starting with 0x because it is the easiest to " convert to. function msgpack#int_dict_to_str(v) abort let v = a:v._VAL " 64-bit number: " 0000000001111111111222222222233333333334444444444555555555566666 " 1234567890123456789012345678901234567890123456789012345678901234 " Split in _VAL: " 0000000001111111111222222222233 3333333344444444445555555555666 66 " 1234567890123456789012345678901 2345678901234567890123456789012 34 " Split by hex digits: " 0000 0000 0111 1111 1112 2222 2222 2333 3333 3334 4444 4444 4555 5555 5556 6666 " 1234 5678 9012 3456 7890 1234 5678 9012 3456 7890 1234 5678 9012 3456 7890 1234 " " Total split: " _VAL[3] _VAL[2] _VAL[1] " ______________________________________ _______________________________________ __ " 0000 0000 0111 1111 1112 2222 2222 233 3 3333 3334 4444 4444 4555 5555 5556 66 66 " 1234 5678 9012 3456 7890 1234 5678 901 2 3456 7890 1234 5678 9012 3456 7890 12 34 " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ " g4 g3 g2 g1 " ********************************** *** * ********************************** ** ** " 1 2 3 4 5 6 " 1: s:mask1(v[3], 28): first 28 bits of _VAL[3] " 2: s:shift(v[3], -28): last 3 bits of _VAL[3] " 3: s:mask1(v[2], 1): first bit of _VAL[2] " 4: s:mask1(s:shift(v[2], -1), 28): bits 2 .. 29 of _VAL[2] " 5: s:shift(v[2], -29): last 2 bits of _VAL[2] " 6: s:shift(v[1], 2): _VAL[1] let g4 = printf('%07x', s:mask1(v[3], 28)) let g3 = printf('%01x', or(s:shift(v[3], -28), s:shift(s:mask1(v[2], 1), 3))) let g2 = printf('%07x', s:mask1(s:shift(v[2], -1), 28)) let g1 = printf('%01x', or(s:shift(v[2], -29), s:shift(v[1], 2))) return ((v[0] < 0 ? '-' : '') . '0x' . g1 . g2 . g3 . g4) endfunction "" " True boolean value. let g:msgpack#true = {'_TYPE': v:msgpack_types.boolean, '_VAL': 1} lockvar! g:msgpack#true "" " False boolean value. let g:msgpack#false = {'_TYPE': v:msgpack_types.boolean, '_VAL': 0} lockvar! g:msgpack#false "" " NIL value. let g:msgpack#nil = {'_TYPE': v:msgpack_types.nil, '_VAL': 0} lockvar! g:msgpack#nil "" " Deduce type of |msgpack-special-dict|. " " @return zero if given dictionary is not special or name of the key in " v:msgpack_types dictionary. function msgpack#special_type(v) abort if type(a:v) != type({}) || !has_key(a:v, '_TYPE') return 0 endif for [k, v] in items(v:msgpack_types) if a:v._TYPE is v return k endif endfor return 0 endfunction "" " Mapping that maps type() output to type names. let s:MSGPACK_STANDARD_TYPES = { \type(0): 'integer', \type(0.0): 'float', \type(''): 'string', \type([]): 'array', \type({}): 'map', \type(v:true): 'boolean', \type(v:null): 'nil', \} "" " Deduce type of one of items returned by msgpackparse(). " " @return Name of a key in v:msgpack_types. function msgpack#type(v) abort let special_type = msgpack#special_type(a:v) if special_type is 0 return s:MSGPACK_STANDARD_TYPES[type(a:v)] endif return special_type endfunction "" " Dump nil value. function s:msgpack_dump_nil(v) abort return 'NIL' endfunction "" " Dump boolean value. function s:msgpack_dump_boolean(v) abort return (a:v is v:true || (a:v isnot v:false && a:v._VAL)) ? 'TRUE' : 'FALSE' endfunction "" " Dump integer msgpack value. function s:msgpack_dump_integer(v) abort if type(a:v) == type({}) return msgpack#int_dict_to_str(a:v) else return string(a:v) endif endfunction "" " Dump floating-point value. function s:msgpack_dump_float(v) abort return substitute(string(type(a:v) == type({}) ? a:v._VAL : a:v), \'\V\^\(-\)\?str2float(''\(inf\|nan\)'')\$', '\1\2', '') endfunction "" " Dump |msgpack-special-dict| that represents a string. If any additional " parameter is given then it dumps binary string. function s:msgpack_dump_string(v) abort if type(a:v) == type({}) let val = a:v else let val = {'_VAL': split(a:v, "\n", 1)} end let ret = ['"'] for v in val._VAL call add( \ret, \substitute( \substitute(v, '["\\]', '\\\0', 'g'), \'\n', '\\0', 'g')) call add(ret, '\n') endfor let ret[-1] = '"' return join(ret, '') endfunction "" " Dump array value. function s:msgpack_dump_array(v) abort let val = type(a:v) == type({}) ? a:v._VAL : a:v return '[' . join(map(val[:], 'msgpack#string(v:val)'), ', ') . ']' endfunction "" " Dump dictionary value. function s:msgpack_dump_map(v) abort let ret = ['{'] if msgpack#special_type(a:v) is 0 for [k, v] in items(a:v) let ret += [s:msgpack_dump_string({'_VAL': split(k, "\n")}), \': ', \msgpack#string(v), \', '] unlet v endfor if !empty(a:v) call remove(ret, -1) endif else for [k, v] in sort(copy(a:v._VAL)) let ret += [msgpack#string(k), \': ', \msgpack#string(v), \', '] unlet k unlet v endfor if !empty(a:v._VAL) call remove(ret, -1) endif endif let ret += ['}'] return join(ret, '') endfunction "" " Dump extension value. function s:msgpack_dump_ext(v) abort return printf('+(%i)%s', a:v._VAL[0], \s:msgpack_dump_string({'_VAL': a:v._VAL[1]})) endfunction "" " Convert msgpack object to a string, like string() function does. Result of the " conversion may be passed to msgpack#eval(). function msgpack#string(v) abort if type(a:v) == type({}) let type = msgpack#special_type(a:v) if type is 0 let type = 'map' endif else let type = get(s:MSGPACK_STANDARD_TYPES, type(a:v), 0) if type is 0 throw printf('msgpack:invtype: Unable to convert value %s', string(a:v)) endif endif return s:msgpack_dump_{type}(a:v) endfunction "" " Copy msgpack object like deepcopy() does, but leave types intact function msgpack#deepcopy(obj) abort if type(a:obj) == type([]) return map(copy(a:obj), 'msgpack#deepcopy(v:val)') elseif type(a:obj) == type({}) let special_type = msgpack#special_type(a:obj) if special_type is 0 return map(copy(a:obj), 'msgpack#deepcopy(v:val)') else return { \'_TYPE': v:msgpack_types[special_type], \'_VAL': msgpack#deepcopy(a:obj._VAL) \} endif else return copy(a:obj) endif endfunction "" " Convert an escaped character to needed value function s:msgpack_eval_str_sub(ch) abort if a:ch is# 'n' return '", "' elseif a:ch is# '0' return '\n' else return '\' . a:ch endif endfunction let s:MSGPACK_SPECIAL_OBJECTS = { \'NIL': '{''_TYPE'': v:msgpack_types.nil, ''_VAL'': 0}', \'TRUE': '{''_TYPE'': v:msgpack_types.boolean, ''_VAL'': 1}', \'FALSE': '{''_TYPE'': v:msgpack_types.boolean, ''_VAL'': 0}', \'nan': '(-(1.0/0.0-1.0/0.0))', \'inf': '(1.0/0.0)', \} "" " Convert msgpack object dumped by msgpack#string() to a Vimscript object " suitable for msgpackdump(). " " @param[in] s String to evaluate. " @param[in] special_objs Additional special objects, in the same format as " s:MSGPACK_SPECIAL_OBJECTS. " " @return Any value that msgpackparse() may return. function msgpack#eval(s, special_objs) abort let s = a:s let expr = [] let context = [] while !empty(s) let s = substitute(s, '^\s*', '', '') if s[0] =~# '\v^\h$' let name = matchstr(s, '\v\C^\w+') if has_key(s:MSGPACK_SPECIAL_OBJECTS, name) call add(expr, s:MSGPACK_SPECIAL_OBJECTS[name]) elseif has_key(a:special_objs, name) call add(expr, a:special_objs[name]) else throw 'name-unknown:Unknown name ' . name . ': ' . s endif let s = s[len(name):] elseif (s[0] is# '-' && s[1] =~# '\v^\d$') || s[0] =~# '\v^\d$' let sign = 1 if s[0] is# '-' let s = s[1:] let sign = -1 endif if s[0:1] is# '0x' " See comment in msgpack#int_dict_to_str(). let s = s[2:] let hexnum = matchstr(s, '\v\C^\x+') if empty(hexnum) throw '0x-empty:Must have number after 0x: ' . s elseif len(hexnum) > 16 throw '0x-long:Must have at most 16 hex digits: ' . s endif let s = s[len(hexnum):] let hexnum = repeat('0', 16 - len(hexnum)) . hexnum let g1 = str2nr(hexnum[0], 16) let g2 = str2nr(hexnum[1:7], 16) let g3 = str2nr(hexnum[8], 16) let g4 = str2nr(hexnum[9:15], 16) let v1 = s:shift(g1, -2) let v2 = or(or(s:shift(s:mask1(g1, 2), 29), s:shift(g2, 1)), \s:mask1(s:shift(g3, -3), 1)) let v3 = or(s:shift(s:mask1(g3, 3), 28), g4) call add(expr, printf('{''_TYPE'': v:msgpack_types.integer, '. \'''_VAL'': [%i, %u, %u, %u]}', \sign, v1, v2, v3)) else let num = matchstr(s, '\v\C^\d+') let s = s[len(num):] if sign == -1 call add(expr, '-') endif call add(expr, num) if s[0] is# '.' let dec = matchstr(s, '\v\C^\.\d+%(e[+-]?\d+)?') if empty(dec) throw '0.-nodigits:Decimal dot must be followed by digit(s): ' . s endif let s = s[len(dec):] call add(expr, dec) endif endif elseif s =~# '\v^\-%(inf|nan)' call add(expr, '-') call add(expr, s:MSGPACK_SPECIAL_OBJECTS[s[1:3]]) let s = s[4:] elseif stridx('="+', s[0]) != -1 let match = matchlist(s, '\v\C^(\=|\+\((\-?\d+)\)|)(\"%(\\.|[^\\"]+)*\")') if empty(match) throw '"-invalid:Invalid string: ' . s endif call add(expr, '{''_TYPE'': v:msgpack_types.') if empty(match[1]) || match[1] is# '=' call add(expr, 'string') else call add(expr, 'ext') endif call add(expr, ', ''_VAL'': [') if match[1][0] is# '+' call add(expr, match[2] . ', [') endif call add(expr, substitute(match[3], '\v\C\\(.)', \'\=s:msgpack_eval_str_sub(submatch(1))', 'g')) if match[1][0] is# '+' call add(expr, ']') endif call add(expr, ']}') let s = s[len(match[0]):] elseif s[0] is# '{' call add(context, 'map') call add(expr, '{''_TYPE'': v:msgpack_types.map, ''_VAL'': [') call add(expr, '[') let s = s[1:] elseif s[0] is# '[' call add(context, 'array') call add(expr, '[') let s = s[1:] elseif s[0] is# ':' call add(expr, ',') let s = s[1:] elseif s[0] is# ',' if context[-1] is# 'array' call add(expr, ',') else call add(expr, '], [') endif let s = s[1:] elseif s[0] is# ']' call remove(context, -1) call add(expr, ']') let s = s[1:] elseif s[0] is# '}' call remove(context, -1) if expr[-1] is# "\x5B" call remove(expr, -1) else call add(expr, ']') endif call add(expr, ']}') let s = s[1:] elseif s[0] is# '''' let char = matchstr(s, '\v\C^\''\zs%(\\\d+|.)\ze\''') if empty(char) throw 'char-invalid:Invalid integer character literal format: ' . s endif if char[0] is# '\' call add(expr, +char[1:]) else call add(expr, char2nr(char)) endif let s = s[len(char) + 2:] else throw 'unknown:Invalid non-space character: ' . s endif endwhile if empty(expr) throw 'empty:Parsed string is empty' endif return eval(join(expr, '')) endfunction "" " Check whether two msgpack values are equal function msgpack#equal(a, b) let atype = msgpack#type(a:a) let btype = msgpack#type(a:b) if atype isnot# btype return 0 endif let aspecial = msgpack#special_type(a:a) let bspecial = msgpack#special_type(a:b) if aspecial is# bspecial if aspecial is# 0 if type(a:a) == type({}) if len(a:a) != len(a:b) return 0 endif if !empty(filter(keys(a:a), '!has_key(a:b, v:val)')) return 0 endif for [k, v] in items(a:a) if !msgpack#equal(v, a:b[k]) return 0 endif unlet v endfor return 1 elseif type(a:a) == type([]) if len(a:a) != len(a:b) return 0 endif let i = 0 for asubval in a:a if !msgpack#equal(asubval, a:b[i]) return 0 endif let i += 1 unlet asubval endfor return 1 elseif type(a:a) == type(0.0) return (a:a == a:a ? a:a == a:b : string(a:a) ==# string(a:b)) else return a:a ==# a:b endif elseif aspecial is# 'map' || aspecial is# 'array' if len(a:a._VAL) != len(a:b._VAL) return 0 endif let alist = aspecial is# 'map' ? sort(copy(a:a._VAL)) : a:a._VAL let blist = bspecial is# 'map' ? sort(copy(a:b._VAL)) : a:b._VAL let i = 0 for asubval in alist let bsubval = blist[i] if aspecial is# 'map' if !(msgpack#equal(asubval[0], bsubval[0]) \&& msgpack#equal(asubval[1], bsubval[1])) return 0 endif else if !msgpack#equal(asubval, bsubval) return 0 endif endif let i += 1 unlet asubval unlet bsubval endfor return 1 elseif aspecial is# 'nil' return 1 elseif aspecial is# 'float' return (a:a._VAL == a:a._VAL \? (a:a._VAL == a:b._VAL) \: (string(a:a._VAL) ==# string(a:b._VAL))) else return a:a._VAL ==# a:b._VAL endif else if atype is# 'array' let a = aspecial is 0 ? a:a : a:a._VAL let b = bspecial is 0 ? a:b : a:b._VAL return msgpack#equal(a, b) elseif atype is# 'string' let a = (aspecial is 0 ? split(a:a, "\n", 1) : a:a._VAL) let b = (bspecial is 0 ? split(a:b, "\n", 1) : a:b._VAL) return a ==# b elseif atype is# 'map' if aspecial is 0 let akeys = copy(a:a) if len(a:b._VAL) != len(akeys) return 0 endif for [k, v] in a:b._VAL if msgpack#type(k) isnot# 'string' " Non-special mapping cannot have non-string keys return 0 endif if type(k) == type({}) if (empty(k._VAL) \|| k._VAL ==# [""] \|| !empty(filter(copy(k._VAL), 'stridx(v:val, "\n") != -1'))) " Non-special mapping cannot have zero byte in key or an empty key return 0 endif let kstr = join(k._VAL, "\n") else let kstr = k endif if !has_key(akeys, kstr) " Protects from both missing and duplicate keys return 0 endif if !msgpack#equal(akeys[kstr], v) return 0 endif call remove(akeys, kstr) unlet k unlet v endfor return 1 else return msgpack#equal(a:b, a:a) endif elseif atype is# 'float' let a = aspecial is 0 ? a:a : a:a._VAL let b = bspecial is 0 ? a:b : a:b._VAL return (a == a ? a == b : string(a) ==# string(b)) elseif atype is# 'integer' if aspecial is 0 let sign = a:a >= 0 ? 1 : -1 let a = sign * a:a let v1 = s:mask1(s:shift(a, -62), 2) let v2 = s:mask1(s:shift(a, -31), 31) let v3 = s:mask1(a, 31) return [sign, v1, v2, v3] == a:b._VAL else return msgpack#equal(a:b, a:a) endif else throw printf('internal-invalid-type: %s == %s, but special %s /= %s', \atype, btype, aspecial, bspecial) endif endif endfunction