LiquidProxy Lua Edition
1--[[
2notice that,
3while this does override the 'string' and add some extra stuff,
4it does not explicitly replace the default string metatable __index
5to do that, require 'ext.meta' (or do it yourself)
6--]]
7local string = {}
8for k,v in pairs(require 'string') do string[k] = v end
9
10local table = require 'ext.table'
11
12-- table.concat(string.split(a,b),b) == a
13function string.split(s, exp)
14 exp = exp or ''
15 s = tostring(s)
16 local t = table()
17 -- handle the exp='' case
18 if exp == '' then
19 for i=1,#s do
20 t:insert(s:sub(i,i))
21 end
22 else
23 local searchpos = 1
24 local start, fin = s:find(exp, searchpos)
25 while start do
26 t:insert(s:sub(searchpos, start-1))
27 searchpos = fin+1
28 start, fin = s:find(exp, searchpos)
29 end
30 t:insert(s:sub(searchpos))
31 end
32 return t
33end
34
35function string.trim(s)
36 return s:match('^%s*(.-)%s*$')
37end
38
39-- should this wrap in a table?
40function string.bytes(s)
41 return table{s:byte(1,#s)}
42end
43
44string.load = load or loadstring
45
46--[[
47-- drifting further from standards...
48-- this string-converts everything concat'd (no more errors, no more print(a,b,c)'s)
49getmetatable('').__concat = function(a,b)
50 return tostring(a)..tostring(b)
51end
52--]]
53
54-- a C++-ized accessor to subsets
55-- indexes are zero-based inclusive
56-- sizes are zero-based-exclusive (or one-based-inclusive depending on how you think about it)
57-- parameters are (index, size) rather than (start index, end index)
58function string.csub(d, start, size)
59 if not size then return string.sub(d, start + 1) end -- til-the-end
60 return string.sub(d, start + 1, start + size)
61end
62
63--d = string data
64--l = length of a column. default 32
65--w = hex word size. default 1
66--c = extra column space. default 8
67function string.hexdump(d, l, w, c)
68 d = tostring(d)
69 l = tonumber(l)
70 w = tonumber(w)
71 c = tonumber(c)
72 if not l or l < 1 then l = 32 end
73 if not w or w < 1 then w = 1 end
74 if not c or c < 1 then c = 8 end
75 local s = table()
76 local rhs = table()
77 local col = 0
78 for i=1,#d,w do
79 if i % l == 1 then
80 s:insert(string.format('%.8x ', (i-1)))
81 rhs = table()
82 col = 1
83 end
84 s:insert' '
85 for j=w,1,-1 do
86 local e = i+j-1
87 local sub = d:sub(e,e)
88 if #sub > 0 then
89 local b = string.byte(sub)
90 s:insert(string.format('%.2x', b))
91 rhs:insert(b >= 32 and sub or '.')
92 end
93 end
94 if col % c == 0 then
95 s:insert' '
96 end
97 if (i + w - 1) % l == 0 or i+w>#d then
98 s:insert' '
99 s:insert(rhs:concat())
100 end
101 if (i + w - 1) % l == 0 then
102 s:insert'\n'
103 end
104 col = col + 1
105 end
106 return s:concat()
107end
108
109-- escape for pattern matching
110local escapeFind = '[' .. ([[^$()%.[]*+-?]]):gsub('.', '%%%1') .. ']'
111function string.patescape(s)
112 return (s:gsub(escapeFind, '%%%1'))
113end
114
115-- this is a common function, especially as a __concat metamethod
116-- it is nearly table.concat, except table.concat errors upon non-string/number instead of calling tostring() automatically
117-- (should I change table.concat's default behavior and use that instead? nah, because why require a table creation.)
118-- tempted to make this ext.op.concat ... but that's specifically a binary op ... and that shouldn't call tostring() while this should ...
119-- maybe I should move this to ext.op as 'tostringconcat' or something?
120function string.concat(...)
121 local n = select('#', ...)
122 if n == 0 then return end -- base-case nil or "" ?
123 local s = tostring((...))
124 if n == 1 then return s end
125 return s .. string.concat(select(2, ...))
126end
127
128-- another common __tostring metamethod
129-- since luajit doesn't support __name metafield
130function string:nametostring()
131 -- NOTICE this will break for anything that overrides its __metatable metafield
132 local mt = getmetatable(self)
133
134 -- invoke a 'rawtostring' call / get the builtin 'tostring' result
135 setmetatable(self, nil)
136 local s = tostring(self)
137 setmetatable(self, mt)
138
139 local name = mt.__name
140 return name and tostring(name)..s:sub(6) or s
141end
142
143-- I use this too often ....
144function string.hex(s, uppercase)
145 local fmt = uppercase and '%02X' or '%02x'
146 return (tostring(s):gsub('.', function(c)
147 return fmt:format(c:byte())
148 end))
149end
150
151function string.unhex(h)
152 if bit.band(#h, 1) == 1
153 or h:find'[^0-9a-fA-F]'
154 then
155 return nil, "string is not hex"
156 end
157 return h:gsub('..', function(d)
158 return string.char(assert(tonumber(d, 16)))
159 end)
160end
161
162-- TODO other encoders?
163
164return string