login

<     >

2021-08-25 14:24:54 (UTC-03:00)

Marcel Rodrigues <marcelgmr@gmail.com>

map: use rice coding for deltas in cache

This cuts ~1/3 of the cache size (in relation to VLPs).

diff --git a/bio.lua b/bio.lua
index 57a06c1..43dba6a 100644
--- a/bio.lua
+++ b/bio.lua
@@ -59,26 +59,13 @@ 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)
-    return u2s(x), u2s(y)
+local function write_byte(fp, n)
+    fp:write(string.char(n))
 end
 
 local function write_beu16(fp, n)
-    fp:write(string.char(bit.rshift(n, 8), bit.band(n, 0xFF)))
+    write_byte(fp, bit.rshift(n, 8))
+    write_byte(fp, bit.band(n, 0xFF))
 end
 
 local function write_beu32(fp, n)
@@ -93,26 +80,89 @@ 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
+local RiceR = {}
+RiceR.__index = RiceR
+
+function RiceR:get_bit()
+    if self.n == 0 then
+        self.b = read_byte(self.fp)
+        self.n = 8
+    end
+    self.n = self.n - 1
+    return bit.band(bit.rshift(self.b, self.n), 1)
+end
+
+function RiceR:get_unsigned()
+    local q = 0
+    while self:get_bit() == 1 do
+        q = q + 1
+    end
+    local r = 0
+    for i = 1, self.k do
+        r = bit.bor(bit.lshift(r, 1), self:get_bit())
+    end
+    return bit.bor(bit.lshift(q, self.k), r)
+end
+
+function RiceR:get_signed()
+    return u2s(self:get_unsigned())
+end
+
+local function rice_r(fp, k)
+    local self = setmetatable({}, RiceR)
+    self.fp = fp    -- already opened file, read mode
+    self.k = k or 1 -- rice parameter
+    self.b = 0      -- value of last byte read
+    self.n = 0      -- number of bits available in self.b
+    return self
+end
+
+local RiceW = {}
+RiceW.__index = RiceW
+
+function RiceW:put_bit(b)
+    self.n = self.n - 1
+    self.b = bit.bor(self.b, bit.lshift(b, self.n))
+    if self.n == 0 then
+        self:flush()
+    end
+end
+
+function RiceW:flush()
+    if self.n ~= 8 then
+        write_byte(self.fp, self.b)
+        self.b = 0
+        self.n = 8
+    end
+end
+
+function RiceW:put_unsigned(n)
+    for i = 1, bit.rshift(n, self.k) do
+        self:put_bit(1)
+    end
+    self:put_bit(0)
+    for i = self.k-1, 0, -1 do
+        self:put_bit(bit.band(bit.rshift(n, i), 1))
+    end
+end
+
+function RiceW:put_signed(n)
+    self:put_unsigned(s2u(n))
 end
 
-local function write_leivlp(fp, x, y)
-    write_leuvlp(fp, s2u(x), s2u(y))
+local function rice_w(fp, k)
+    local self = setmetatable({}, RiceW)
+    self.fp = fp    -- already opened file, write mode
+    self.k = k or 1 -- rice parameter
+    self.b = 0      -- value of next byte to write
+    self.n = 8      -- number of bits available in self.b
+    return self
 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, 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
+    read_led64=read_led64, write_byte=write_byte, write_beu16=write_beu16,
+    write_beu32=write_beu32, write_bei16=write_bei16, rice_r=rice_r, rice_w=rice_w
 }

diff --git a/map.lua b/map.lua
index f04fd09..9d1a189 100644
--- a/map.lua
+++ b/map.lua
@@ -249,17 +249,22 @@ function Frame:save_cache(fname, sf, filter)
         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_leivlp(cache, ox, oy)
+            bio.write_bei16(cache, ox)
+            bio.write_bei16(cache, oy)
+            local rice = bio.rice_w(cache)
             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_leivlp(cache, dx, dy)
+                    rice:put_signed(dx)
+                    rice:put_signed(dy)
                     ox, oy = x, y
                 end
             end
-            bio.write_leivlp(cache, 0, 0)
+            rice:put_signed(0)
+            rice:put_signed(0)
+            rice:flush()
         end
     end
     cache:close()
@@ -362,12 +367,13 @@ function Cache:get_polys(key)
     return function()
         if npolys > 0 then
             npolys = npolys - 1
-            local ox, oy = bio.read_leivlp(cache)
+            local ox, oy = bio.read_bei16(cache), bio.read_bei16(cache)
+            local rice = bio.rice_r(cache)
             local x, y = 0, 0
             return function()
                 if x ~= ox or y ~= oy then
                     x, y = ox, oy
-                    local dx, dy = bio.read_leivlp(cache)
+                    local dx, dy = rice:get_signed(), rice:get_signed()
                     ox, oy = ox+dx, oy+dy
                     return {x, y}
                 end