login

<     >

2021-08-24 12:22:12 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

map: make caches smaller by using VLPs

VLP = variable-length pair

Each byte in a  VLP uses high nibble to store X  and low nibble to
store Y.  Signed is encoded as  unsigned by using odd  numbers for
positive and even numbers for negative.

Usually 95+% of points are compressed to 1 byte per point.

This is not as optimal as Rice Coding, but probably simpler, since
we don't have to deal with bit streams.

diff --git a/bio.lua b/bio.lua
index 73f3c5c..c2c12f6 100644
--- a/bio.lua
+++ b/bio.lua
@@ -49,6 +49,26 @@ local function read_led64(fp)
     return ffi.cast("double *", ffi.new("char[8]", fp:read(8)))[0]
 end
 
+local function read_leuvlp(fp)
+    local x, y = 0, 0
+    local s = 0
+    repeat
+        local byte = fp:read(1):byte(1)
+        l, r = bit.band(bit.rshift(byte, 4), 0x07), bit.band(byte, 0x07)
+        x = bit.bor(bit.lshift(l, s), x)
+        y = bit.bor(bit.lshift(r, s), y)
+        s = s + 3
+    until bit.band(byte, 0x88) == 0
+    return x, y
+end
+
+local function read_leivlp(fp)
+    local x, y = read_leuvlp(fp)
+    if bit.band(x, 1) == 1 then x = (x+1)/2 elseif x ~= 0 then x = -(x/2) end
+    if bit.band(y, 1) == 1 then y = (y+1)/2 elseif y ~= 0 then y = -(y/2) end
+    return x, y
+end
+
 local function write_beu16(fp, n)
     fp:write(string.char(bit.rshift(n, 8), bit.band(n, 0xFF)))
 end
@@ -65,10 +85,28 @@ local function write_bei16(fp, n)
     write_beu16(fp, n)
 end
 
+local function write_leuvlp(fp, x, y)
+    repeat
+        local byte = bit.bor(bit.lshift(bit.band(x, 0x07), 4), bit.band(y, 0x07))
+        x, y = bit.rshift(x, 3), bit.rshift(y, 3)
+        if x ~= 0 or y ~= 0 then
+            byte = bit.bor(byte, 0x88)
+        end
+        fp:write(string.char(byte))
+    until x == 0 and y == 0
+end
+
+local function write_leivlp(fp, x, y)
+    if x < 0 then x = -2*x elseif x > 0 then x = x*2-1 end
+    if y < 0 then y = -2*y elseif y > 0 then y = y*2-1 end
+    write_leuvlp(fp, x, y)
+end
+
 return {
     read_byte=read_byte, read_leu16=read_leu16, read_leu32=read_leu32,
     read_beu16=read_beu16, read_beu32=read_beu32, read_lei16=read_lei16,
     read_lei32=read_lei32, read_bei16=read_bei16, read_bei32=read_bei32,
-    read_led64=read_led64, write_beu16=write_beu16, write_beu32=write_beu32,
-    write_bei16=write_bei16
+    read_led64=read_led64, read_leuvlp=read_leuvlp, read_leivlp=read_leivlp,
+    write_beu16=write_beu16, write_beu32=write_beu32, write_bei16=write_bei16,
+    write_leuvlp=write_leuvlp, write_leivlp=write_leivlp
 }

diff --git a/map.lua b/map.lua
index 4619421..f04fd09 100644
--- a/map.lua
+++ b/map.lua
@@ -245,24 +245,22 @@ function Frame:save_cache(fname, sf, filter)
         bio.write_beu32(cache, offset)
         cache:seek("set", offset)
         local bb, lens, polys = sf:read_polygons(index)
+        bio.write_beu16(cache, #lens)
         for poly in self:mapped(polys) do
             local ox, oy = unpack(poly())
             ox, oy = math.floor(ox + 0.5), math.floor(oy + 0.5)
-            bio.write_bei16(cache, ox)
-            bio.write_bei16(cache, oy)
+            bio.write_leivlp(cache, ox, oy)
             for point in poly do
                 local x, y = unpack(point)
                 x, y = math.floor(x + 0.5), math.floor(y + 0.5)
                 local dx, dy = x-ox, y-oy
                 if dx ~= 0 or dy ~= 0 then
-                    bio.write_bei16(cache, dx)
-                    bio.write_bei16(cache, dy)
+                    bio.write_leivlp(cache, dx, dy)
                     ox, oy = x, y
                 end
             end
-            bio.write_beu32(cache, 0) -- end list of points
+            bio.write_leivlp(cache, 0, 0)
         end
-        bio.write_beu32(cache, 0x80008000) -- end list of polys
     end
     cache:close()
 end
@@ -360,14 +358,16 @@ function Cache:get_polys(key)
     end
     assert(offset > 0, ("key '%s' not found in cache '%s'"):format(key, fname))
     cache:seek("set", offset)
+    local npolys = bio.read_beu16(cache)
     return function()
-        local ox, oy = bio.read_bei16(cache), bio.read_bei16(cache)
-        if ox ~= -0x8000 or oy ~= -0x8000 then
+        if npolys > 0 then
+            npolys = npolys - 1
+            local ox, oy = bio.read_leivlp(cache)
             local x, y = 0, 0
             return function()
                 if x ~= ox or y ~= oy then
                     x, y = ox, oy
-                    local dx, dy = bio.read_bei16(cache), bio.read_bei16(cache)
+                    local dx, dy = bio.read_leivlp(cache)
                     ox, oy = ox+dx, oy+dy
                     return {x, y}
                 end