[루아2.4] table

4 분 소요

table.c

전체적으로 변경이 많습니다. 코드 가독성을 높인 변경들이 주로 눈에 띕니다. 그 외에는 소소한 변경이네요.

[2.2]
static Word lua_ntable = 0;
static Word lua_nconstant = 0;

[2.4]
Word lua_ntable = 0;
Word lua_nconstant = 0;

정적 변수였던 테이블 갯수 관련 카운터 변수에서 static을 뺐습니다. 디버깅 정보 출력하는 함수에서 이 변수값을 가져가려고 static을 뺐네요.

[2.2]
static void lua_initsymbol (void)
{
 Word n;
 lua_maxsymbol = BUFFER_BLOCK;
 lua_table = newvector(lua_maxsymbol, Symbol);
 n = luaI_findsymbolbyname("next");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_next;
 n = luaI_findsymbolbyname("dofile");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_internaldofile;
 n = luaI_findsymbolbyname("setglobal");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = setglobal;
 n = luaI_findsymbolbyname("getglobal");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = getglobal;
 n = luaI_findsymbolbyname("nextvar");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_nextvar;
 n = luaI_findsymbolbyname("type"); 
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = luaI_type;
 n = luaI_findsymbolbyname("tonumber");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_obj2number;
 n = luaI_findsymbolbyname("print");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_print;
 n = luaI_findsymbolbyname("dostring");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = lua_internaldostring;
 n = luaI_findsymbolbyname("setfallback");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = luaI_setfallback;
 n = luaI_findsymbolbyname("error");
 s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = luaI_error;
}

[2.4]
static struct {
  char *name;
  lua_CFunction func;
} int_funcs[] = {
  {"assert", luaI_assert},
  {"dofile", lua_internaldofile},
  {"dostring", lua_internaldostring},
  {"error", luaI_error},
  {"getglobal", luaI_getglobal},
  {"next", lua_next},
  {"nextvar", lua_nextvar},
  {"print", luaI_print},
  {"setfallback", luaI_setfallback},
  {"setglobal", luaI_setglobal},
  {"tonumber", lua_obj2number},
  {"tostring", luaI_tostring},
  {"type", luaI_type}
};

#define INTFUNCSIZE (sizeof(int_funcs)/sizeof(int_funcs[0]))


void luaI_initsymbol (void)
{
  int i;
  lua_maxsymbol = BUFFER_BLOCK;
  lua_table = newvector(lua_maxsymbol, Symbol);
  for (i=0; i<INTFUNCSIZE; i++)
  {
    Word n = luaI_findsymbolbyname(int_funcs[i].name);
    s_tag(n) = LUA_T_CFUNCTION; s_fvalue(n) = int_funcs[i].func;
  }
}

루아 2.2에서 함수 코드로 일일이 집어 넣은 심볼 테이블 초기화 데이터를 구조체 배열을 미리 선언한 다음 for 루프로 코딩해 넣었습니다. 결과는 동일합니다. 그냥 보기에 루아 2.4에서 변경한 코드가 더 나아보입니다만, 제 개인적으로는 루아 2.2 코드도 나쁘지 않아 보입니다. 루아 2.4 스타일 코드가 확실하게 비교 우위를 가지려면 int_funcs 구조체 배열을 최소한 두 군데 이상에서 사용해야 하는데 그냥 luaI_initsymbol() 함수에서만 쓸 뿐이거든요. 그러면 코드 사이즈든 가독성면에서든 루아 2.2 스타일 코드에 비해 딱히 장점이 없습니다. 뭐 초기화 데이터의 수정이 빈번하거나 초기화 데이터를 외부에서 받아 오는것이 아닌이상 일회용으로 쓰는 코드의 리터럴 데이터를 밖으로 빼서 데이터와 코드로 구분하는 스타일은 구조적으로 깔끔하고 칭찬할만한 일이나 그냥 하드 코딩하는 것 역시 그리 심하게 나쁜 코드라고 보기 어렵습니다.

[2.2]
void lua_initconstant (void)
{
 lua_maxconstant = BUFFER_BLOCK;
 lua_constant = newvector(lua_maxconstant, TaggedString *);
}

[2.4]
void luaI_initconstant (void)
{
 lua_maxconstant = BUFFER_BLOCK;
 lua_constant = newvector(lua_maxconstant, TaggedString *);
 /* pre-register mem error messages, to avoid loop when error arises */
 luaI_findconstantbyname(tableEM);
 luaI_findconstantbyname(memEM);
}

#define tableEM  "table overflow"
#define memEM "not enough memory"

luaI_initconstant() 함수에서 에러 메시지 두 개를 초기화할 때 넣습니다. tableEM과 memEM은 문자열 리터럴로 mem.h 파일에 선언했습니다. 에러 메시지 문자열을 루아 문자열 객체로 처리하려는가 봅니다. 굳이 이럴 필요가 없어 보이는데 왜 이런 결정을 한 걸까요.

[2.2]
Word luaI_findsymbol (TreeNode *t)
{
 if (lua_table == NULL)
  lua_initsymbol(); 
 if (t->varindex == NOT_USED)
 {
  if (lua_ntable == lua_maxsymbol)
  {
   if (lua_maxsymbol >= MAX_WORD)
     lua_error("symbol table overflow");
   lua_maxsymbol *= 2;
   if (lua_maxsymbol >= MAX_WORD)
     lua_maxsymbol = MAX_WORD; 
   lua_table = growvector(lua_table, lua_maxsymbol, Symbol);
  }
  t->varindex = lua_ntable;
  s_tag(lua_ntable) = LUA_T_NIL;
  lua_ntable++;
 }
 return t->varindex;
}

[2.4]
Word luaI_findsymbol (TaggedString *t)
{
 if (t->varindex == NOT_USED)
 {
  if (lua_ntable == lua_maxsymbol)
    lua_maxsymbol = growvector(&lua_table, lua_maxsymbol, Symbol,
                      symbolEM, MAX_WORD);
  t->varindex = lua_ntable;
  lua_table[lua_ntable].varname = t;
  s_tag(lua_ntable) = LUA_T_NIL;
  lua_ntable++;
 }
 return t->varindex;
}

심볼 테이블에서 심볼을 찾는 함수가 짧아졌습니다. 기본적인 동작은 같고 growvector() 함수의 파라메터 형식을 바꿔서 growvector() 함수를 사용하는 함수들의 코드를 모두 전반적으로 짧게 바꿨습니다.

TaggedString *luaI_createfixedstring (char *name)
{
  TaggedString *ts = lua_createstring(name);
  if (!ts->marked)
    ts->marked = 2;  /* avoid GC */
  return ts;
}

루아 2.4에서 새로 만든 함수입니다. 함수 이름만 보면 고정 문자열을 만드는 함수입니다. 내용을 보면 기존에 루아 코드에서 보이는 문자열 객체 생성하는 코드 패턴이 나오고 이어서 marked를 2로 설정해서 가비지 컬랙션 대상에서 뺍니다. 따라서 이 함수로 만든 문자열 객체는 루아 VM에서 메모리 해제되지 않고 계속 존재합니다.

[2.2]
void lua_pack (void)
{
  static Long block = GARBAGE_BLOCK; /* when garbage collector will be called */
  static Long nentity = 0;  /* counter of new entities (strings and arrays) */
  Long recovered = 0;
  if (nentity++ < block) return;
  lua_travstack(lua_markobject); /* mark stack objects */
  lua_travsymbol(lua_markobject); /* mark symbol table objects */
  luaI_travlock(lua_markobject); /* mark locked objects */
  luaI_travfallbacks(lua_markobject);  /* mark fallbacks */
  recovered += lua_strcollector();
  recovered += lua_hashcollector();
  recovered += luaI_funccollector();
  nentity = 0;				/* reset counter */
  block=(16*block-7*recovered)/12;	/* adapt block size */
  if (block < MIN_GARBAGE_BLOCK) block = MIN_GARBAGE_BLOCK;
} 

[2.4]
Long luaI_collectgarbage (void)
{
  Long recovered = 0;
  lua_travstack(lua_markobject); /* mark stack objects */
  lua_travsymbol(lua_markobject); /* mark symbol table objects */
  luaI_travlock(lua_markobject); /* mark locked objects */
  luaI_travfallbacks(lua_markobject);  /* mark fallbacks */
  luaI_invalidaterefs();
  recovered += lua_strcollector();
  recovered += lua_hashcollector();
  recovered += luaI_funccollector();
  return recovered;
} 

void lua_pack (void)
{
  static unsigned long block = GARBAGE_BLOCK;
  static unsigned long nentity = 0;  /* total of strings, arrays, etc */
  unsigned long recovered = 0;
  if (nentity++ < block) return;
  recovered = luaI_collectgarbage();
  block = block*2*(1.0 - (float)recovered/nentity);
  nentity -= recovered;
} 

가비지 컬랙션을 수행하는 lua_pack() 함수를 루아 2.4에서 두 개로 나눴습니다. 동작 자체는 같지만 함수를 둘로 나눠서 역할을 명확히 했고 의미적으로 읽기 편해졌습니다.

댓글남기기