lua-users home
lua-l archive

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


Hi List,
as I reported in my "Low RAM report" 1 week ago:

I am just working on a "string buffer" lib, which uses
"length-constrained strings" (maximum 250 bytes) for "heavy text work"
without any allocation of new string elements.

[Side remark: During this work last week I recognized quite a terrible
misunderstanding in my code. I had recognized that after 2 invokes of
garbagecollect the by 60kB heap RAM memory ran out... now I found a
possible error for this: By some stupid error I unfortunately used at
2 "regularly happening positions (during any yield/resume cycle)"
new/delete for tiny struct data in my own C code, this somehow "of
course" was NOT really intended... Like this in "competition to Lua
gc", also my own C code invoked malloc quite often ... and this then
of course made successful life for Lua gc nearly impossible with such
restricted RAM... I did not check this completely, I will need some
more days to clarify this and report then... Now I overwrote the new
and delete operators with assert(0) constructions, so that I will now
be sure that I do not use new/delete any more... . (I am generally
used to this - in Controller C code I usually NEVER use dynamic memory
- so no general problem to reserve all my heap for Lua)...]

But separately from this, when I work with my strbuf elements, I first
had the following problem, if I want to do the following lua code:

Buf=sb.setup( 100)               -- defines new stringbuf "Buf" with
100 byte length
Buf='hallo'

Now of course I would like that by string buffer Buffer contains the
the text 'hallo'. But unfortunately after this, my buffer Buf will NOT
exist anymore, but instead will have been "converted" to a string by
Lua... . With the metafunctions __index / __newindex, I fixed this in
a FIRST approach like this:

Buf=sb.setup( 100)               -- defines new stringbuf "Buf" with
100 byte length
Buf[1]='hallo'                         -- "...[1]" required, to keep Buf "alive"

... but is really rather dangerous. There is always some risk that the
user by some accident would use Buf='hallo' (or Buf=Buf1), I by
accident did this quite fast myself... ... and all this Buf should
survive, just then of course should have updated the contents of Buf.

Now I found a very nice solution for this problem - at least for
global string buffers (this restriction is fine for me, I do not want
them local...). After installing metatable for _G, with __index and
__newindex for _G (see my other post some days ago and hint of Sean),
I can quite easily replace any variable "SB..." by "__SB...", so that
SB... will NOT exist in _G, and thus then G__newindex will be invoked
EVERY time, write SBbuf=anything... . This is nice then and solves all
my problems with this:

static int G__index (lua_State *L) {
  const char* pcName= lua_tostring( L, 2);
  if( pcName && pcName[0]== 'S' && pcName[1]== 'B'){
    // access to strbuf SB...: replace by __SB...
    char ac__Name[20+3]= "__";
    int iLen= strlen( pcName);
    luaL_argexpected( L, iLen <= sizeof( ac__Name)-3, 2, "valid");
    memcpy( ac__Name+2, pcName, iLen+1);
    lua_pushstring( L, ac__Name);
    int iTyp= lua_rawget( L, 1); // get __SB... data
    assert( iTyp== LUA_TUSERDATA); // just for security
    return 1; // and return this
  }
  // standard __index functionality otherwise:
  lua_pushvalue( L, -1);
  lua_rawget( L, 1);
  return 1;
}


static int G__newindex (lua_State *L) {
  LPCSTR pcName= lua_tostring( L, 2);
  if( pcName && pcName[0]== 'S' && pcName[1]== 'B'){
    // assign to strbuf SB...: either init __SB..., or invoke __SB[nil]
    char ac__Name[20+3]= "__";
    int iLen= strlen( pcName);
    luaL_argexpected( L, iLen <= sizeof( ac__Name)-3, 2, "valid");
    memcpy( ac__Name+2, pcName, iLen+1);
    lua_pushstring( L, ac__Name);
    int iTyp= lua_rawget( L, 1); // get __SB... from _G if available
    if( iTyp<= LUA_TNIL){
      //not yet available: __SB... init
      lua_pop( L, 1);
      lua_pushstring( L, ac__Name);
      luaL_checkudata(L, 3, "strbuf"); // value must be valid strbuf
      lua_pushvalue( L, 3);
      lua_rawset( L, 1);  // _G("__SB...")= value
      assert( lua_gettop( L)== 3);
      return 0;
    }
    // __SB... available: call sb__newindex(L) with correct stack pars
    luaL_checkudata(L, -1, "strbuf"); // __SB... must be valid strbuf
    lua_replace( L, 1);
    lua_pushnil( L);
    lua_replace( L, 2);
    assertIfDebug( lua_gettop( L)== 3);
    return sb__newindex(L); // __SB...[nil]=value (strbuf/nil/string)
  }
  // otherwise: Do standard action of G__newaction... :
  lua_pushvalue( L, -2);
  lua_pushvalue( L, -2);
  lua_rawset( L, 1);
  assertIfDebug( lua_gettop( L)== 3);
  return 0;
}



Only two conditions remain:
- this works only for globals (not for locals), but this is fine for
me - I will allow my sb.setup
  function to work only for globals.
- the string buffer names (and only them) now must start in some
"predefined way" SB..., but
  this also fine for me - I do not see a problem here for my
application (and I check this in the
  G__index / G__newindex, this only is some 3-4 lines more very easy code.

... I am very happy with this so far ... if anybody sees any problems,
please give me a note... . I hope some other people also might like
this / think this is useful for them.