util-pck.lua /size: 4119 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['util-pck'] = {
2    version   = 1.001,
3    comment   = "companion to luat-lib.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9-- moved from core-uti
10
11local next, tostring, type = next, tostring, type
12local sort, concat = table.sort, table.concat
13local format = string.format
14local sortedhashkeys, sortedkeys, tohash = table.sortedhashkeys, table.sortedkeys, table.tohash
15
16utilities         = utilities         or { }
17utilities.packers = utilities.packers or { }
18local packers     = utilities.packers
19packers.version   = 1.01
20
21local fmt_kv = JITSUPPORTED and "%s=%s"   or "%s=%q"
22local fmt_kt = JITSUPPORTED and "%s={%s}" or "%s={%q}"
23
24local function hashed(t)
25    local s, ns = { }, 0
26    for k, v in next, t do
27        ns = ns + 1
28        if type(v) == "table" then
29            s[ns] = format(fmt_kt,k,hashed(v))
30        else
31            s[ns] = format(fmt_kv,k,v)
32        end
33    end
34    sort(s)
35    return concat(s,",")
36end
37
38local function simplehashed(t)
39    local s, ns = { }, 0
40    for k, v in next, t do
41        ns = ns + 1
42        s[ns] = format(fmt_kv,k,v)
43    end
44    sort(s)
45    return concat(s,",")
46end
47
48packers.hashed       = hashed
49packers.simplehashed = simplehashed
50
51-- In luatex < 0.74 (lua 5.1) a next chain was the same for each run so no sort was needed,
52-- but in the latest greatest versions (lua 5.2) we really need to sort the keys in order
53-- not to get endless runs due to a difference in tuc files.
54
55local function pack(t,keys,skip,hash,index)
56    if t then
57        local sk = #t > 0 and sortedkeys(t) or sortedhashkeys(t)
58        for i=1,#sk do
59            local k = sk[i]
60            if not skip or not skip[k] then
61                local v = t[k]
62                if type(v) == "table" then
63                    pack(v,keys,skip,hash,index)
64                    if keys[k] then
65                        local h = hashed(v)
66                        local i = hash[h]
67                        if not i then
68                            i = #index + 1
69                            index[i] = v
70                            hash[h] = i
71                        end
72                        t[k] = i
73                    end
74                end
75            end
76        end
77    end
78end
79
80local function unpack(t,keys,skip,index)
81    if t then
82        for k, v in next, t do
83            if keys[k] and type(v) == "number" then
84                local iv = index[v]
85                if iv then
86                    v = iv
87                    t[k] = v
88                end
89            end
90            if type(v) == "table" and (not skip or not skip[k]) then
91                unpack(v,keys,skip,index)
92            end
93        end
94    end
95end
96
97function packers.new(keys,version,skip)
98    return {
99        version = version or packers.version,
100        keys    = tohash(keys),
101        skip    = tohash(skip),
102        hash    = { },
103        index   = { },
104    }
105end
106
107function packers.pack(t,p,shared)
108    if shared then
109        pack(t,p.keys,p.skip,p.hash,p.index)
110    elseif not t.packer then
111        pack(t,p.keys,p.skip,p.hash,p.index)
112        if #p.index > 0 then
113            t.packer = {
114                version = p.version or packers.version,
115                keys    = p.keys,
116                skip    = p.skip,
117                index   = p.index,
118            }
119        end
120        p.hash  = { }
121        p.index = { }
122    end
123end
124
125function packers.unpack(t,p,shared)
126    if shared then
127        if p then
128            unpack(t,p.keys,p.skip,p.index)
129        end
130    else
131        local tp = t.packer
132        if tp then
133            if tp.version == (p and p.version or packers.version) then
134                unpack(t,tp.keys,tp.skip,tp.index)
135            else
136               return false
137            end
138            t.packer = nil
139        end
140    end
141    return true
142end
143
144function packers.strip(p)
145    p.hash = nil
146end
147
148-- We could have a packer.serialize where we first flush the shared table
149-- and then use inline a reference . This saves an unpack.
150