util-pck.lmt /size: 4767 b    last modification: 2021-10-28 13:51
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
21-- local fmt_kv <const> = "%s=%q"
22-- local fmt_kt <const> = "%s={%q}"
23
24-- local 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,",")
36-- end
37
38local function hashed(t) --local function tabstr_normal(t)
39    local s = { }
40    local n = 0
41    for k, v in next, t do
42        n = n + 1
43        if type(v) == "table" then
44            s[n] = k .. ">" .. hashed(v)
45        elseif v == true then
46            s[n] = k .. "+" -- "=true"
47        elseif v then
48            s[n] = k .. "=" .. v
49        else
50            s[n] = k .. "-" -- "=false"
51        end
52    end
53    if n == 0 then
54        return ""
55    elseif n == 1 then
56        return s[1]
57    else
58        sort(s) -- costly but needed (occasional wrong hit otherwise)
59        return concat(s,",")
60    end
61end
62
63local function simplehashed(t)
64    local s = { }
65    local n = 0
66    for k, v in next, t do
67        n = n + 1
68     -- s[n] = format(fmt_kv,k,v)
69        s[n] = k .. "=" .. v
70    end
71    sort(s)
72    return concat(s,",")
73end
74
75packers.hashed       = hashed
76packers.simplehashed = simplehashed
77
78-- In luatex < 0.74 (lua 5.1) a next chain was the same for each run so no sort was needed,
79-- but in the latest greatest versions (lua 5.2) we really need to sort the keys in order
80-- not to get endless runs due to a difference in tuc files.
81
82local function pack(t,keys,skip,hash,index)
83    if t then
84        local sk = #t > 0 and sortedkeys(t) or sortedhashkeys(t)
85        for i=1,#sk do
86            local k = sk[i]
87            if not skip or not skip[k] then
88                local v = t[k]
89                if type(v) == "table" then
90                    pack(v,keys,skip,hash,index)
91                    if keys[k] then
92                        local h = hashed(v)
93                        local i = hash[h]
94                        if not i then
95                            i = #index + 1
96                            index[i] = v
97                            hash[h] = i
98                        end
99                        t[k] = i
100                    end
101                end
102            end
103        end
104    end
105end
106
107local function unpack(t,keys,skip,index)
108    if t then
109        for k, v in next, t do
110            if keys[k] and type(v) == "number" then
111                local iv = index[v]
112                if iv then
113                    v = iv
114                    t[k] = v
115                end
116            end
117            if type(v) == "table" and (not skip or not skip[k]) then
118                unpack(v,keys,skip,index)
119            end
120        end
121    end
122end
123
124function packers.new(keys,version,skip)
125    return {
126        version = version or packers.version,
127        keys    = tohash(keys),
128        skip    = tohash(skip),
129        hash    = { },
130        index   = { },
131    }
132end
133
134function packers.pack(t,p,shared)
135    if shared then
136        pack(t,p.keys,p.skip,p.hash,p.index)
137    elseif not t.packer then
138        pack(t,p.keys,p.skip,p.hash,p.index)
139        if #p.index > 0 then
140            t.packer = {
141                version = p.version or packers.version,
142                keys    = p.keys,
143                skip    = p.skip,
144                index   = p.index,
145            }
146        end
147        p.hash  = { }
148        p.index = { }
149    end
150end
151
152function packers.unpack(t,p,shared)
153    if shared then
154        if p then
155            unpack(t,p.keys,p.skip,p.index)
156        end
157    else
158        local tp = t.packer
159        if tp then
160            if tp.version == (p and p.version or packers.version) then
161                unpack(t,tp.keys,tp.skip,tp.index)
162            else
163               return false
164            end
165            t.packer = nil
166        end
167    end
168    return true
169end
170
171function packers.strip(p)
172    p.hash = nil
173end
174
175-- We could have a packer.serialize where we first flush the shared table
176-- and then use inline a reference . This saves an unpack.
177