Почему внутренние строки Lua хранят так, как они делают? - PullRequest
8 голосов
/ 24 января 2012

Мне нужна простая таблица строк, в которой будет храниться куча констант, и я подумал: «Привет! Луа делает это, позвольте мне использовать некоторые из этих функций!»

Это в основном в lstring.hФайлы /lstring.c (я использую 5.2)

Сначала я покажу интересующий меня код.Это из lobject.h

/*
** Header for string value; string bytes follow the end of this structure
*/
typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;  /* number of characters in string */
  } tsv;
} TString;


/* get the actual string (array of bytes) from a TString */
#define getstr(ts)  cast(const char *, (ts) + 1)

/* get the actual string (array of bytes) from a Lua value */
#define svalue(o)       getstr(rawtsvalue(o))

Как видите, данные хранятся вне структуры.Чтобы получить поток байтов, вы берете размер TString, добавляете 1 и получаете указатель char *.

Разве это не плохое кодирование?Его сверлили в m в моих классах C, чтобы сделать четко определенные структуры.Я знаю, что здесь может быть гнездо, но вы действительно теряете столько скорости / пространства, определяя структуру как заголовок для данных, а не определяя значение указателя для этих данных?

Ответы [ 3 ]

5 голосов
/ 24 января 2012

Идея, вероятно, заключается в том, что вы размещаете заголовок и данные в одном большом фрагменте данных вместо двух:

TString *str = (TString*)malloc(sizeof(TString) + <length_of_string>);

В дополнение к одному вызову malloc / free вы также уменьшаете памятьфрагментация и увеличение локализации памяти.

Но, отвечая на ваш вопрос, да, подобные взломы, как правило, являются плохой практикой и должны выполняться с особой осторожностью.И если вы это сделаете, вы, вероятно, захотите скрыть их под слоем макросов / встроенных функций.

2 голосов
/ 24 января 2012

Как говорит Родриго, идея состоит в том, чтобы выделить заголовок и строковые данные как один фрагмент памяти.Стоит отметить, что вы также видите нестандартный хак

struct lenstring {
  unsigned length;
  char data[0];
};

, но C99 добавил гибкие элементы массива, так что это можно сделать стандартным образом, как

struct lenstring {
  unsigned length;
  char data[];
};

Если Lua'sСтрока была сделана таким образом, это было бы что-то вроде

typedef union TString {
  L_Umaxalign dummy;
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
    const char data[];
  } tsv;
} TString;

#define getstr(ts) (ts->tsv->data)
1 голос
/ 21 декабря 2014

Это относится к осложнениям, связанным с более ограниченным языком Си. В C ++ вы бы просто определили базовый класс с именем GCObject, который содержит переменные для сборки мусора, тогда TString будет подклассом, и с помощью виртуального деструктора оба элемента TString и сопровождающие его блоки const char * будут освобожден должным образом.

Когда речь идет о написании такого же вида функциональности в C, это немного сложнее, так как классы и виртуальное наследование не существуют.

То, что делает Lua, реализует сборку мусора, вставляя заголовок, необходимый для управления состоянием сбора мусора части памяти, следующей за ней. Помните, что free(void *) не нужно знать ничего, кроме адреса блока памяти.

#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked

Lua хранит связанный список этих «собираемых» блоков памяти, в данном случае массив символов, так что он может эффективно освободить память, не зная тип объекта, на который он указывает.

Если ваш TString указал на другой блок памяти, в котором находился массив символов, то он требует, чтобы сборщик мусора определил тип объекта, а затем углубился в его структуру, чтобы также освободил строковый буфер.

Псевдокод для такого рода сборки мусора будет выглядеть примерно так:

GCHeader *next, *prev;
GCHeader *current = firstObject;

while(current)
{
    next = current->next;
    if (/* current is ready for deletion */)
    {
        free(current);

        // relink previous to the next (singly-linked list)
        if (prev)
            prev->next = next;
    }
    else
        prev = current; // store previous undeleted object
    current = next;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...