lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


One thing I've been musing about, with respect to strings, is
exemplified by the following function in llex.c:

TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
  lua_State *L = ls->L;
  TString *ts = luaS_newlstr(L, str, l);
  TValue *o = luaH_setstr(L, ls->fs->h, ts);  /* entry for `str' */
  if (ttisnil(o))
    setbvalue(o, 1);  /* make sure `str' will not be collected */
  return ts;
}

This function is used to temporarily stash a string into a hash table
(whose address is at ls->fs->h) in order to avoid it being garbage
collected during the parse.

Doing that with the standard API is quite a lot more verbose. Here's
one way:

static const char *stash_string (lua_State *L, int table_index,
                                 const char *str, size_t len) {
  const char *rv;
  lua_pushlstring(L, str, len);
  lua_pushvalue(L, -1); /* In case we need it again */
  rv = lua_tostring(L, -1);
  lua_rawget(L, table_index);
  if (lua_isnil(L, -1)) {
    lua_pop(L, 1);
    lua_pushboolean(L, 1);
    lua_rawset(L, table_index);
  }
  else {
    lua_pop(L, 2);
  }
  return rv;
}

This requires 6 or 8 API calls, which compares rather unfavourably
with the optimized (internal) version in llex.c; my tests seem to
indicate that the execution of index2adr in lapi.c is a major
bottleneck.

This could be improved considerably with the API call
lua_haskey(L, index), which looks up the key at the top of
the stack in the table at 'index' and returns a boolean
without popping the key.

It would also be nice if lua_pushlstring returned a pointer
to the (interned) string.

Then the above could be written:

const char *stash_string (lua_State *L, int table_index,
                          const char *str, size_t len) {
  const char *rv = lua_pushlstring(L, str, len);
  if (lua_haskey(L, table_index))
    lua_pop(L, 1);
  else {
    lua_pushboolean(L, 1);
    lua_rawset(L, table_index);
  }
  return rv;
}

This cuts the number of API calls down to 3 or 4, and also
makes it easier to do default values:

  if (!lua_haskey(L, table_index)) {
    lua_pushvalue(L, -1);
    /* push default value, eg:
     *      lua_newtable(L);
     *  or
     *      lua_pushinteger(L, 0);
     */
    lua_rawset(L, table_index);
  }
  lua_rawget(L, table_index);