사용자 도구

사이트 도구


kb:luacustomchunkreader

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:luacustomchunkreader [2014/11/06 17:01] (현재)
줄 1: 줄 1:
 +====== Lua Custom Chunk Reader ======
 +[[VirtualFileSystem]] 안에 있는 파일에서 루아 파일을 읽어들이기. ​
 +
 +생각해 보면 여러 가지 방법이 있다. ​
 +
 +  - 팩 파일 안에다 루아 텍스트 파일을 두고, 그걸 모두 메모리로 읽어들여서,​ dostring 혹은 luaL_loadbuffer 함수를 이용한다.
 +  - 팩 파일 안에다 루아 텍스트/​청크 파일을 두고, 그걸 임시 디렉토리로 빼낸 다음, dofile 혹은 luaL_loadfile 함수를 이용한다.
 +  - 팩 파일 안에다 루아 텍스트/​청크 파일을 두고, 사용자 읽기 함수를 제공하여,​ lua_load 함수를 호출한다.
 +
 +여기서 다루고자 하는 것은, 마지막 경우다. 나름대로 정석적인 방법?
 +
 +
 +====== lua_load/​lua_Reader ======
 +<code cpp>
 +typedef const char* (*lua_Reader)(lua_State *L, void* data, size_t* size);
 +int lua_load (lua_State* L, lua_Reader reader, void* data, const char* chunkname);
 +</​code>​
 +
 +lua_load 함수는 팩 파일 같은 걸 사용하지 않는 이상, 직접 호출할 일은 거의 없는 함수다. luaL_loadbuffer,​ loadL_loadfile 함수 내부에서 알아서 처리해주기 때문이다. lauxlib.c 파일에 이 두 함수가 구현되어 있는데, 바이너리 파일을 읽어들이는 부분만 옮겨보자면 다음과 같다.
 +
 +<code cpp>
 +typedef struct LoadF 
 +{
 +    int extraline;
 +    FILE *f;
 +    char buff[LUAL_BUFFERSIZE];​
 +} LoadF;
 +
 +static const char *getF (lua_State *L, void *ud, size_t *size) ​
 +{
 +    LoadF *lf = (LoadF *)ud;
 +    (void)L;
 +    if (lf->​extraline) ​
 +    {
 +        lf->​extraline = 0;
 +        *size = 1;
 +        return "​\n";​
 +    }
 +    if (feof(lf->​f)) return NULL;
 +    *size = fread(lf->​buff,​ 1, LUAL_BUFFERSIZE,​ lf->f);
 +    return (*size > 0) ? lf->buff : NULL;
 +}
 +
 +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) ​
 +{
 +    LoadF lf;
 +    int status, readstatus;
 +    int c;
 +    int fnameindex = lua_gettop(L) + 1;  /* index of filename on the stack */
 +    lf.extraline = 0;
 +    if (filename == NULL) 
 +    {
 +        lua_pushliteral(L,​ "​=stdin"​);​
 +        lf.f = stdin;
 +    }
 +    else 
 +    {
 +        lua_pushfstring(L,​ "​@%s",​ filename);
 +        lf.f = fopen(filename,​ "​r"​);​
 +        if (lf.f == NULL) return errfile(L, "​open",​ fnameindex);​
 +    }
 +    ​
 +    c = getc(lf.f);
 +    if (c == '#'​) /* Unix exec. file? */
 +    {  ​
 +        lf.extraline = 1;
 +        while ((c = getc(lf.f)) != EOF && c != '​\n'​) ;  /* skip first line */
 +        if (c == '​\n'​) c = getc(lf.f);
 +    }
 +    if (c == LUA_SIGNATURE[0] && lf.f != stdin) /* binary file? */
 +    {  ​
 +        fclose(lf.f);​
 +        lf.f = fopen(filename,​ "​rb"​); ​ /* reopen in binary mode */
 +        if (lf.f == NULL) return errfile(L, "​reopen",​ fnameindex);​
 +        /* skip eventual `#​!...'​ */
 +        while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
 +        lf.extraline = 0;
 +    }
 +    ungetc(c, lf.f);
 +    ​
 +    status = lua_load(L, getF, &lf, lua_tostring(L,​ -1));
 +    readstatus = ferror(lf.f);​
 +    if (lf.f != stdin) fclose(lf.f); ​ /* close file (even in case of errors) */
 +    if (readstatus) ​
 +    {
 +        lua_settop(L,​ fnameindex); ​ /* ignore results from `lua_load'​ */
 +        return errfile(L, "​read",​ fnameindex);​
 +    }
 +    lua_remove(L,​ fnameindex);​
 +    return status;
 +}
 +</​code>​
 +getF 라는 함수가 청크 리더 함수다. 파일 포인터를 저장하고,​ 함수 호출이 들어올 때마다 파일에서 내용을 읽어들여 버퍼에다 복사한 다음, 버퍼 포인터를 반환하는 걸 볼 수 있다. 파일에 더 이상 읽어들일 것이 없다면 NULL을 반환한다.
 +
 +즉 lua_Reader 함수에서 해야하는 일은...
 +
 +  * 인수로 받은 유저 데이터 포인터를 이용해, 어디서부터 데이터를 읽어들여야 할지 판단한다.
 +  * 스트림에 남은 데이터가 있다면 size 변수 값을 남은 데이터의 길이로 세팅하고,​ 데이터 포인터 제일 앞쪽을 반환한다.
 +  * 스트림에 남은 데이터가 없다면 NULL을 반환한다.
 +
 +이걸 참고로 사용자 함수를 만들어 보자면...
 +<code cpp>
 +struct MEMORY_FILE
 +{
 +    char* start; ​    // 가상 파일 내용의 맨 앞쪽 포인터
 +    size_t total; ​   // 가상 파일의 전체 길이
 +    size_t offset; ​  // 현재까지 읽어들인 데이터의 길이
 +    char buff[1024]; // 이번에 읽어들인 데이터를 복사한 버퍼
 +};
 +
 +static const char* MemoryReader(lua_State* L, void* ud, size_t* size)
 +{
 +    MEMORY_FILE* file = reinterpret_cast<​MEMORY_FILE*>​(ud);​
 +
 +    *size = std::​min(file->​total - file->​offset,​ 1024);
 +    if (*size > 0)
 +    {
 +        memcpy(file->​buff,​ file->​start + file->​offset,​ *size);
 +        file->​offset += *size;
 +        return file->​buff;​
 +    }
 +    else
 +    {
 +        return NULL;
 +    }
 +}
 +
 +int my_loadfile(lua_State* L, vfs* filesystem, const char* filename) ​
 +{
 +    MEMORY_FILE file;
 +    file.start = vfs->​get_file_start(filename);​ // VFS에서 파일을 읽어들였다고 치자.
 +    file.total = vfs->​get_file_size(filename);​ // VFS에서 파일 사이즈를 알아냈다고 치자.
 +    file.offset = 0;
 +    return lua_load(L, MemoryReader,​ &file, filename);
 +}
 +</​code>​
 +대충 이런 느낌? 쓸데없이 메모리에 복사를 하고 있는데, 그냥 사용법을 표시하기 위해...
 +
 +
 +====== 문제점 ======
 +모듈 경로는 도대체 어떻게 되는 것이냐!
 +----
 +  * see also [[Lua]]
  
kb/luacustomchunkreader.txt · 마지막으로 수정됨: 2014/11/06 17:01 (바깥 편집)