Browse Source

Fixed buffers save long strings as external.

Roberto Ierusalimschy 1 year ago
parent
commit
3b57e37e48
4 changed files with 29 additions and 16 deletions
  1. 1 1
      ldump.c
  2. 15 8
      lundump.c
  3. 4 2
      manual/manual.of
  4. 9 5
      testes/api.lua

+ 1 - 1
ldump.c

@@ -126,7 +126,7 @@ static void dumpString (DumpState *D, TString *ts) {
       size_t size;
       size_t size;
       const char *s = getlstr(ts, size);
       const char *s = getlstr(ts, size);
       dumpSize(D, size + 2);
       dumpSize(D, size + 2);
-      dumpVector(D, s, size);
+      dumpVector(D, s, size + 1);  /* include ending '\0' */
       D->nstr++;  /* one more saved string */
       D->nstr++;  /* one more saved string */
       setsvalue(D->L, &key, ts);  /* the string is the key */
       setsvalue(D->L, &key, ts);  /* the string is the key */
       setivalue(&value, D->nstr);  /* its index is the value */
       setivalue(&value, D->nstr);  /* its index is the value */

+ 15 - 8
lundump.c

@@ -147,17 +147,24 @@ static TString *loadStringN (LoadState *S, Proto *p) {
     luaH_getint(S->h, idx, &stv);
     luaH_getint(S->h, idx, &stv);
     return tsvalue(&stv);
     return tsvalue(&stv);
   }
   }
-  else if (size -= 2, size <= LUAI_MAXSHORTLEN) {  /* short string? */
-    char buff[LUAI_MAXSHORTLEN];
-    loadVector(S, buff, size);  /* load string into buffer */
+  else if ((size -= 2) <= LUAI_MAXSHORTLEN) {  /* short string? */
+    char buff[LUAI_MAXSHORTLEN + 1];  /* extra space for '\0' */
+    loadVector(S, buff, size + 1);  /* load string into buffer */
     ts = luaS_newlstr(L, buff, size);  /* create string */
     ts = luaS_newlstr(L, buff, size);  /* create string */
   }
   }
   else {  /* long string */
   else {  /* long string */
-    ts = luaS_createlngstrobj(L, size);  /* create string */
-    setsvalue2s(L, L->top.p, ts);  /* anchor it ('loadVector' can GC) */
-    luaD_inctop(L);
-    loadVector(S, getlngstr(ts), size);  /* load directly in final place */
-    L->top.p--;  /* pop string */
+    if (S->fixed) {  /* for a fixed buffer, use a fixed string */
+      const char *s = getaddr(S, size + 1, char);  /* get content address */
+      ts = luaS_newextlstr(L, s, size, NULL, NULL);
+    }
+    else {  /* create internal copy */
+      ts = luaS_createlngstrobj(L, size);  /* create string */
+      setsvalue2s(L, L->top.p, ts);  /* anchor it ('loadVector' can GC) */
+      luaD_inctop(L);
+      loadVector(S, getlngstr(ts), size);  /* load directly in final place */
+      loadByte(S);  /* skip ending '\0' */
+      L->top.p--;  /* pop string */
+    }
   }
   }
   luaC_objbarrier(L, p, ts);
   luaC_objbarrier(L, p, ts);
   S->nstr++;  /* add string to list of saved strings */
   S->nstr++;  /* add string to list of saved strings */

+ 4 - 2
manual/manual.of

@@ -3651,8 +3651,10 @@ Moreover, it may have a @Char{B} instead of a @Char{b},
 meaning a @emphx{fixed buffer} with the binary dump.
 meaning a @emphx{fixed buffer} with the binary dump.
 
 
 A fixed buffer means that the address returned by the reader function
 A fixed buffer means that the address returned by the reader function
-should contain the chunk until everything created by the chunk has
-been collected.
+will contain the chunk until everything created by the chunk has
+been collected;
+therefore, Lua can avoid copying to internal structures
+some parts of the chunk.
 (In general, a fixed buffer would keep the chunk
 (In general, a fixed buffer would keep the chunk
 as its contents until the end of the program,
 as its contents until the end of the program,
 for instance with the chunk in ROM.)
 for instance with the chunk in ROM.)

+ 9 - 5
testes/api.lua

@@ -528,13 +528,15 @@ do
   local N = 1000
   local N = 1000
   -- create a somewhat "large" source
   -- create a somewhat "large" source
   for i = 1, N do source[i] = "X = X + 1; " end
   for i = 1, N do source[i] = "X = X + 1; " end
+  -- add a long string to the source
+  source[#source + 1] = string.format("Y = '%s'", string.rep("a", N));
   source = table.concat(source)
   source = table.concat(source)
   -- give chunk an explicit name to avoid using source as name
   -- give chunk an explicit name to avoid using source as name
   source = load(source, "name1")
   source = load(source, "name1")
   -- dump without debug information
   -- dump without debug information
   source = string.dump(source, true)
   source = string.dump(source, true)
-  -- each "X=X+1" generates 4 opcodes with 4 bytes each
-  assert(#source > N * 4 * 4)
+  -- each "X=X+1" generates 4 opcodes with 4 bytes each, plus the string
+  assert(#source > N * 4 * 4 + N)
   collectgarbage(); collectgarbage()
   collectgarbage(); collectgarbage()
   local m1 = collectgarbage"count" * 1024
   local m1 = collectgarbage"count" * 1024
   -- load dump using fixed buffer
   -- load dump using fixed buffer
@@ -544,9 +546,11 @@ do
   ]], source)
   ]], source)
   collectgarbage()
   collectgarbage()
   local m2 = collectgarbage"count" * 1024
   local m2 = collectgarbage"count" * 1024
-  -- load used fewer than 300 bytes
-  assert(m2 > m1 and m2 - m1 < 300)
-  X = 0; code(); assert(X == N); X = nil
+  -- load used fewer than 350 bytes. Code alone has more than 3*N bytes,
+  -- and string literal has N bytes. Both were not loaded.
+  assert(m2 > m1 and m2 - m1 < 350)
+  X = 0; code(); assert(X == N and Y == string.rep("a", N))
+  X = nil; Y = nil
 end
 end