Patch 8.1.1043
Problem:    Lua interface does not support Blob.
Solution:   Add support to Blob. (Ozaki Kiichi, closes #4151)
Files:      runtime/doc/if_lua.txt, src/if_lua.c, src/testdir/test_lua.vim



*** ../vim-8.1.1042/runtime/doc/if_lua.txt      2018-07-01 19:49:23.605793273 
+0200
--- runtime/doc/if_lua.txt      2019-03-23 13:53:32.131159759 +0100
***************
*** 10,20 ****
  2. The vim module             |lua-vim|
  3. List userdata              |lua-list|
  4. Dict userdata              |lua-dict|
! 5. Funcref userdata           |lua-funcref|
! 6. Buffer userdata            |lua-buffer|
! 7. Window userdata            |lua-window|
! 8. The luaeval function               |lua-luaeval|
! 9. Dynamic loading            |lua-dynamic|
  
  {Vi does not have any of these commands}
  
--- 10,21 ----
  2. The vim module             |lua-vim|
  3. List userdata              |lua-list|
  4. Dict userdata              |lua-dict|
! 5. Blob userdata              |lua-blob|
! 6. Funcref userdata           |lua-funcref|
! 7. Buffer userdata            |lua-buffer|
! 8. Window userdata            |lua-window|
! 9. luaeval() Vim function     |lua-luaeval|
! 10. Dynamic loading           |lua-dynamic|
  
  {Vi does not have any of these commands}
  
***************
*** 125,133 ****
                                Non-numeric keys are not used to initialize
                                the list. See also |lua-eval| for conversion
                                rules. Example: >
!                                       :lua t = {math.pi, false, say = 'hi'}
!                                       :echo luaeval('vim.list(t)')
!                                       :" [3.141593, v:false], 'say' is ignored
  <
        vim.dict([arg])         Returns an empty dictionary or, if "arg" is a
                                Lua table, returns a dict d such that d[k] =
--- 126,134 ----
                                Non-numeric keys are not used to initialize
                                the list. See also |lua-eval| for conversion
                                rules. Example: >
!                                   :lua t = {math.pi, false, say = 'hi'}
!                                   :echo luaeval('vim.list(t)')
!                                   :" [3.141593, v:false], 'say' is ignored
  <
        vim.dict([arg])         Returns an empty dictionary or, if "arg" is a
                                Lua table, returns a dict d such that d[k] =
***************
*** 136,147 ****
                                strings. Keys that are not strings are not
                                used to initialize the dictionary. See also
                                |lua-eval| for conversion rules. Example: >
!                                       :lua t = {math.pi, false, say = 'hi'}
!                                       :echo luaeval('vim.dict(t)')
!                                       :" {'say': 'hi'}, numeric keys ignored
  <
        vim.funcref({name})     Returns a Funcref to function {name} (see
!                               |Funcref|). It is equivalent to Vim's 
function().
  
        vim.buffer([arg])       If "arg" is a number, returns buffer with
                                number "arg" in the buffer list or, if "arg"
--- 137,158 ----
                                strings. Keys that are not strings are not
                                used to initialize the dictionary. See also
                                |lua-eval| for conversion rules. Example: >
!                                   :lua t = {math.pi, false, say = 'hi'}
!                                   :echo luaeval('vim.dict(t)')
!                                   :" {'1': 3.141593, '2': v:false,
!                                   :" 'say': 'hi'}
! <
!       vim.blob([arg])         Returns an empty blob or, if "arg" is a Lua
!                               string, returns a blob b such that b is
!                               equivalent to "arg" as a byte string.
!                               Examples: >
!                                   :lua s = "12ab\x00\x80\xfe\xff"
!                                   :echo luaeval('vim.blob(s)')
!                                   :" 0z31326162.0080FEFF
  <
        vim.funcref({name})     Returns a Funcref to function {name} (see
!                               |Funcref|). It is equivalent to Vim's
!                               function().
  
        vim.buffer([arg])       If "arg" is a number, returns buffer with
                                number "arg" in the buffer list or, if "arg"
***************
*** 258,264 ****
  <
  
  ==============================================================================
! 5. Funcref userdata                                   *lua-funcref*
  
  Funcref userdata represent funcref variables in Vim. Funcrefs that were
  defined with a "dict" attribute need to be obtained as a dictionary key
--- 269,302 ----
  <
  
  ==============================================================================
! 5. Blob userdata                                      *lua-blob*
! 
! Blob userdata represent vim blobs. A blob "b" has the following properties:
! 
! Properties
! ----------
!       o "#b" is the length of blob "b", equivalent to "len(b)" in Vim.
!       o "b[k]" returns the k-th item in "b"; "b" is zero-indexed, as in Vim.
!           To modify the k-th item, simply do "b[k] = number"; in particular,
!           "b[#b] = number" can append a byte to tail.
! 
! Methods
! -------
!       o "b:add(bytes)" appends "bytes" to the end of "b".
! 
! Examples:
! >
!       :let b = 0z001122
!       :lua b = vim.eval('b') -- same 'b'
!       :lua print(b, b[0], #b)
!       :lua b[1] = 32
!       :lua b[#b] = 0x33 -- append a byte to tail
!       :lua b:add("\x80\x81\xfe\xff")
!       :echo b
! <
! 
! ==============================================================================
! 6. Funcref userdata                                   *lua-funcref*
  
  Funcref userdata represent funcref variables in Vim. Funcrefs that were
  defined with a "dict" attribute need to be obtained as a dictionary key
***************
*** 291,297 ****
  <
  
  ==============================================================================
! 6. Buffer userdata                                    *lua-buffer*
  
  Buffer userdata represent vim buffers. A buffer userdata "b" has the following
  properties and methods:
--- 329,335 ----
  <
  
  ==============================================================================
! 7. Buffer userdata                                    *lua-buffer*
  
  Buffer userdata represent vim buffers. A buffer userdata "b" has the following
  properties and methods:
***************
*** 343,349 ****
  <
  
  ==============================================================================
! 7. Window userdata                                    *lua-window*
  
  Window objects represent vim windows. A window userdata "w" has the following
  properties and methods:
--- 381,387 ----
  <
  
  ==============================================================================
! 8. Window userdata                                    *lua-window*
  
  Window objects represent vim windows. A window userdata "w" has the following
  properties and methods:
***************
*** 375,381 ****
  <
  
  ==============================================================================
! 8. The luaeval function                                       *lua-luaeval* 
*lua-eval*
  
  The (dual) equivalent of "vim.eval" for passing Lua values to Vim is
  "luaeval". "luaeval" takes an expression string and an optional argument and
--- 413,419 ----
  <
  
  ==============================================================================
! 9. luaeval() Vim function                             *lua-luaeval* *lua-eval*
  
  The (dual) equivalent of "vim.eval" for passing Lua values to Vim is
  "luaeval". "luaeval" takes an expression string and an optional argument and
***************
*** 388,397 ****
        end
  <
  Note that "_A" receives the argument to "luaeval". Lua numbers, strings, and
! list, dict, and funcref userdata are converted to their Vim respective types,
! while Lua booleans are converted to numbers. An error is thrown if conversion
! of any of the remaining Lua types, including userdata other than lists, dicts,
! and funcrefs, is attempted.
  
  Examples: >
  
--- 426,435 ----
        end
  <
  Note that "_A" receives the argument to "luaeval". Lua numbers, strings, and
! list, dict, blob, and funcref userdata are converted to their Vim respective
! types, while Lua booleans are converted to numbers. An error is thrown if
! conversion of any of the remaining Lua types, including userdata other than
! lists, dicts, blobs, and funcrefs, is attempted.
  
  Examples: >
  
***************
*** 406,412 ****
  
  
  ==============================================================================
! 9. Dynamic loading                                *lua-dynamic*
  
  On MS-Windows and Unix the Lua library can be loaded dynamically.  The
  |:version| output then includes |+lua/dyn|.
--- 444,450 ----
  
  
  ==============================================================================
! 10. Dynamic loading                               *lua-dynamic*
  
  On MS-Windows and Unix the Lua library can be loaded dynamically.  The
  |:version| output then includes |+lua/dyn|.
*** ../vim-8.1.1042/src/if_lua.c        2019-03-19 21:59:16.268914799 +0100
--- src/if_lua.c        2019-03-23 13:53:32.131159759 +0100
***************
*** 28,33 ****
--- 28,34 ----
  typedef win_T *luaV_Window;
  typedef dict_T *luaV_Dict;
  typedef list_T *luaV_List;
+ typedef blob_T *luaV_Blob;
  typedef struct {
      char_u    *name;  // funcref
      dict_T    *self;  // selfdict
***************
*** 36,41 ****
--- 37,43 ----
  
  static const char LUAVIM_DICT[] = "dict";
  static const char LUAVIM_LIST[] = "list";
+ static const char LUAVIM_BLOB[] = "blob";
  static const char LUAVIM_FUNCREF[] = "funcref";
  static const char LUAVIM_BUFFER[] = "buffer";
  static const char LUAVIM_WINDOW[] = "window";
***************
*** 68,73 ****
--- 70,76 ----
  
  static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
  static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
+ static luaV_Blob *luaV_pushblob(lua_State *L, blob_T *blo);
  static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name);
  
  #if LUA_VERSION_NUM <= 501
***************
*** 541,546 ****
--- 544,552 ----
        case VAR_FUNC:
            luaV_pushfuncref(L, tv->vval.v_string);
            break;
+       case VAR_BLOB:
+           luaV_pushblob(L, tv->vval.v_blob);
+           break;
        default:
            lua_pushnil(L);
      }
***************
*** 582,624 ****
        {
            void *p = lua_touserdata(L, pos);
  
!           if (lua_getmetatable(L, pos)) /* has metatable? */
            {
!               /* check list */
                luaV_getfield(L, LUAVIM_LIST);
                if (lua_rawequal(L, -1, -2))
                {
                    tv->v_type = VAR_LIST;
                    tv->vval.v_list = *((luaV_List *) p);
                    ++tv->vval.v_list->lv_refcount;
!                   lua_pop(L, 2); /* MTs */
                    break;
                }
!               /* check dict */
                luaV_getfield(L, LUAVIM_DICT);
                if (lua_rawequal(L, -1, -3))
                {
                    tv->v_type = VAR_DICT;
                    tv->vval.v_dict = *((luaV_Dict *) p);
                    ++tv->vval.v_dict->dv_refcount;
!                   lua_pop(L, 3); /* MTs */
                    break;
                }
!               /* check funcref */
!               luaV_getfield(L, LUAVIM_FUNCREF);
                if (lua_rawequal(L, -1, -4))
                {
                    luaV_Funcref *f = (luaV_Funcref *) p;
                    func_ref(f->name);
                    tv->v_type = VAR_FUNC;
                    tv->vval.v_string = vim_strsave(f->name);
!                   lua_pop(L, 4); /* MTs */
                    break;
                }
!               lua_pop(L, 4); /* MTs */
            }
        }
!       /* FALLTHROUGH */
        default:
            tv->v_type = VAR_NUMBER;
            tv->vval.v_number = 0;
--- 588,640 ----
        {
            void *p = lua_touserdata(L, pos);
  
!           if (lua_getmetatable(L, pos)) // has metatable?
            {
!               // check list
                luaV_getfield(L, LUAVIM_LIST);
                if (lua_rawequal(L, -1, -2))
                {
                    tv->v_type = VAR_LIST;
                    tv->vval.v_list = *((luaV_List *) p);
                    ++tv->vval.v_list->lv_refcount;
!                   lua_pop(L, 2); // MTs
                    break;
                }
!               // check dict
                luaV_getfield(L, LUAVIM_DICT);
                if (lua_rawequal(L, -1, -3))
                {
                    tv->v_type = VAR_DICT;
                    tv->vval.v_dict = *((luaV_Dict *) p);
                    ++tv->vval.v_dict->dv_refcount;
!                   lua_pop(L, 3); // MTs
                    break;
                }
!               // check blob
!               luaV_getfield(L, LUAVIM_BLOB);
                if (lua_rawequal(L, -1, -4))
                {
+                   tv->v_type = VAR_BLOB;
+                   tv->vval.v_blob = *((luaV_Blob *) p);
+                   ++tv->vval.v_blob->bv_refcount;
+                   lua_pop(L, 4); // MTs
+                   break;
+               }
+               // check funcref
+               luaV_getfield(L, LUAVIM_FUNCREF);
+               if (lua_rawequal(L, -1, -5))
+               {
                    luaV_Funcref *f = (luaV_Funcref *) p;
                    func_ref(f->name);
                    tv->v_type = VAR_FUNC;
                    tv->vval.v_string = vim_strsave(f->name);
!                   lua_pop(L, 5); // MTs
                    break;
                }
!               lua_pop(L, 4); // MTs
            }
        }
!       // FALLTHROUGH
        default:
            tv->v_type = VAR_NUMBER;
            tv->vval.v_number = 0;
***************
*** 753,759 ****
  luaV_list_len(lua_State *L)
  {
      list_T *l = luaV_unbox(L, luaV_List, 1);
!     lua_pushinteger(L, (l == NULL) ? 0 : (int) l->lv_len);
      return 1;
  }
  
--- 769,775 ----
  luaV_list_len(lua_State *L)
  {
      list_T *l = luaV_unbox(L, luaV_List, 1);
!     lua_pushinteger(L, (int) list_len(l));
      return 1;
  }
  
***************
*** 909,915 ****
  luaV_dict_len(lua_State *L)
  {
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
!     lua_pushinteger(L, (d == NULL) ? 0 : (int) d->dv_hashtab.ht_used);
      return 1;
  }
  
--- 925,931 ----
  luaV_dict_len(lua_State *L)
  {
      dict_T *d = luaV_unbox(L, luaV_Dict, 1);
!     lua_pushinteger(L, (int) dict_len(d));
      return 1;
  }
  
***************
*** 1029,1034 ****
--- 1045,1168 ----
  };
  
  
+ /* =======   Blob type   ======= */
+ 
+     static luaV_Blob *
+ luaV_newblob(lua_State *L, blob_T *blo)
+ {
+     luaV_Blob *b = (luaV_Blob *) lua_newuserdata(L, sizeof(luaV_Blob));
+     *b = blo;
+     blo->bv_refcount++; /* reference in Lua */
+     luaV_setudata(L, blo); /* cache[blo] = udata */
+     luaV_getfield(L, LUAVIM_BLOB);
+     lua_setmetatable(L, -2);
+     return b;
+ }
+ 
+ luaV_pushtype(blob_T, blob, luaV_Blob)
+ luaV_type_tostring(blob, LUAVIM_BLOB)
+ 
+     static int
+ luaV_blob_gc(lua_State *L)
+ {
+     blob_T *b = luaV_unbox(L, luaV_Blob, 1);
+     blob_unref(b);
+     return 0;
+ }
+ 
+     static int
+ luaV_blob_len(lua_State *L)
+ {
+     blob_T *b = luaV_unbox(L, luaV_Blob, 1);
+     lua_pushinteger(L, (int) blob_len(b));
+     return 1;
+ }
+ 
+     static int
+ luaV_blob_index(lua_State *L)
+ {
+     blob_T *b = luaV_unbox(L, luaV_Blob, 1);
+     if (lua_isnumber(L, 2))
+     {
+       int idx = luaL_checkinteger(L, 2);
+       if (idx < blob_len(b))
+           lua_pushnumber(L, (lua_Number) blob_get(b, idx));
+       else
+           lua_pushnil(L);
+     }
+     else if (lua_isstring(L, 2))
+     {
+       const char *s = lua_tostring(L, 2);
+       if (strncmp(s, "add", 3) == 0)
+       {
+           lua_getmetatable(L, 1);
+           lua_getfield(L, -1, s);
+       }
+       else
+           lua_pushnil(L);
+     }
+     else
+       lua_pushnil(L);
+     return 1;
+ }
+ 
+     static int
+ luaV_blob_newindex(lua_State *L)
+ {
+     blob_T *b = luaV_unbox(L, luaV_Blob, 1);
+     if (b->bv_lock)
+       luaL_error(L, "blob is locked");
+     if (lua_isnumber(L, 2))
+     {
+       long len = blob_len(b);
+       int idx = luaL_checkinteger(L, 2);
+       int val = luaL_checkinteger(L, 3);
+       if (idx < len || (idx == len && ga_grow(&b->bv_ga, 1) == OK))
+       {
+           blob_set(b, idx, (char_u) val);
+           if (idx == len)
+               ++b->bv_ga.ga_len;
+       }
+       else
+           luaL_error(L, "index out of range");
+     }
+     return 0;
+ }
+ 
+     static int
+ luaV_blob_add(lua_State *L)
+ {
+     luaV_Blob *blo = luaV_checkudata(L, 1, LUAVIM_BLOB);
+     blob_T *b = (blob_T *) luaV_checkcache(L, (void *) *blo);
+     if (b->bv_lock)
+       luaL_error(L, "blob is locked");
+     lua_settop(L, 2);
+     if (!lua_isstring(L, 2))
+       luaL_error(L, "string expected, got %s", luaL_typename(L, 2));
+     else
+     {
+       size_t i, l = 0;
+       const char *s = lua_tolstring(L, 2, &l);
+ 
+       ga_grow(&b->bv_ga, l);
+       for (i = 0; i < l; ++i)
+           ga_append(&b->bv_ga, s[i]);
+     }
+     lua_settop(L, 1);
+     return 1;
+ }
+ 
+ static const luaL_Reg luaV_Blob_mt[] = {
+     {"__tostring", luaV_blob_tostring},
+     {"__gc", luaV_blob_gc},
+     {"__len", luaV_blob_len},
+     {"__index", luaV_blob_index},
+     {"__newindex", luaV_blob_newindex},
+     {"add", luaV_blob_add},
+     {NULL, NULL}
+ };
+ 
+ 
  /* =======   Funcref type   ======= */
  
      static luaV_Funcref *
***************
*** 1624,1629 ****
--- 1758,1790 ----
  }
  
      static int
+ luaV_blob(lua_State *L)
+ {
+     blob_T *b;
+     int initarg = !lua_isnoneornil(L, 1);
+ 
+     if (initarg && !lua_isstring(L, 1))
+       luaL_error(L, "string expected, got %s", luaL_typename(L, 1));
+     b = blob_alloc();
+     if (b == NULL)
+       lua_pushnil(L);
+     else
+     {
+       luaV_newblob(L, b);
+       if (initarg)
+       {
+           size_t i, l = 0;
+           const char *s = lua_tolstring(L, 1, &l);
+ 
+           ga_grow(&b->bv_ga, l);
+           for (i = 0; i < l; ++i)
+               ga_append(&b->bv_ga, s[i]);
+       }
+     }
+     return 1;
+ }
+ 
+     static int
  luaV_funcref(lua_State *L)
  {
      const char *name = luaL_checkstring(L, 1);
***************
*** 1717,1722 ****
--- 1878,1889 ----
                lua_pushstring(L, "dict");
                return 1;
            }
+           luaV_getfield(L, LUAVIM_BLOB);
+           if (lua_rawequal(L, -1, 2))
+           {
+               lua_pushstring(L, "blob");
+               return 1;
+           }
            luaV_getfield(L, LUAVIM_FUNCREF);
            if (lua_rawequal(L, -1, 2))
            {
***************
*** 1748,1753 ****
--- 1915,1921 ----
      {"line", luaV_line},
      {"list", luaV_list},
      {"dict", luaV_dict},
+     {"blob", luaV_blob},
      {"funcref", luaV_funcref},
      {"buffer", luaV_buffer},
      {"window", luaV_window},
***************
*** 1883,1888 ****
--- 2051,2059 ----
      luaV_newmetatable(L, LUAVIM_DICT);
      lua_pushvalue(L, 1);
      luaV_openlib(L, luaV_Dict_mt, 1);
+     luaV_newmetatable(L, LUAVIM_BLOB);
+     lua_pushvalue(L, 1);
+     luaV_openlib(L, luaV_Blob_mt, 1);
      luaV_newmetatable(L, LUAVIM_FUNCREF);
      lua_pushvalue(L, 1);
      luaV_openlib(L, luaV_Funcref_mt, 1);
*** ../vim-8.1.1042/src/testdir/test_lua.vim    2019-03-19 21:59:16.268914799 
+0100
--- src/testdir/test_lua.vim    2019-03-23 13:53:32.131159759 +0100
***************
*** 50,55 ****
--- 50,60 ----
    call assert_equal('dict', luaeval('vim.type(v)'))
    call assert_equal({'a':'b'}, luaeval('v'))
  
+   " lua.eval with a blob
+   lua v = vim.eval("0z00112233.deadbeef")
+   call assert_equal('blob', luaeval('vim.type(v)'))
+   call assert_equal(0z00112233.deadbeef, luaeval('v'))
+ 
    call assert_fails('lua v = vim.eval(nil)',
          \ "[string \"vim chunk\"]:1: bad argument #1 to 'eval' (string 
expected, got nil)")
    call assert_fails('lua v = vim.eval(true)',
***************
*** 428,433 ****
--- 433,462 ----
    lua str, d = nil
  endfunc
  
+ func Test_blob()
+   call assert_equal(0z, luaeval('vim.blob("")'))
+   call assert_equal(0z31326162, luaeval('vim.blob("12ab")'))
+   call assert_equal(0z00010203, luaeval('vim.blob("\x00\x01\x02\x03")'))
+   call assert_equal(0z8081FEFF, luaeval('vim.blob("\x80\x81\xfe\xff")'))
+ 
+   lua b = vim.blob("\x00\x00\x00\x00")
+   call assert_equal(0z00000000, luaeval('b'))
+   call assert_equal(4.0, luaeval('#b'))
+   lua b[0], b[1], b[2], b[3] = 1, 32, 256, 0xff
+   call assert_equal(0z012000ff, luaeval('b'))
+   lua b[4] = string.byte("z", 1)
+   call assert_equal(0z012000ff.7a, luaeval('b'))
+   call assert_equal(5.0, luaeval('#b'))
+   call assert_fails('lua b[#b+1] = 0x80', '[string "vim chunk"]:1: index out 
of range')
+   lua b:add("12ab")
+   call assert_equal(0z012000ff.7a313261.62, luaeval('b'))
+   call assert_equal(9.0, luaeval('#b'))
+   call assert_fails('lua b:add(nil)', '[string "vim chunk"]:1: string 
expected, got nil')
+   call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string 
expected, got boolean')
+   call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string 
expected, got table')
+   lua b = nil
+ endfunc
+ 
  func Test_funcref()
    function I(x)
      return a:x
*** ../vim-8.1.1042/src/version.c       2019-03-23 13:30:19.251356505 +0100
--- src/version.c       2019-03-23 13:54:37.582657565 +0100
***************
*** 777,778 ****
--- 777,780 ----
  {   /* Add new patch number below this line */
+ /**/
+     1043,
  /**/

-- 
To be rich is not the end, but only a change of worries.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui