-- vim: ft=terra
local m = {
shorthand = {maxlen = 14}
}
local pstring = lib.mem.ptr(int8)
-- swap in place -- faster on little endian
m.netswap_ip = macro(function(ty, src, dest)
if ty:astype().type ~= 'integer' then error('bad type') end
local bytes = ty:astype().bytes
src = `[&uint8](src)
dest = `[&uint8](dest)
if config.endian == 'little' then
return quote for i = 0, bytes do dest[i] = src[bytes - (i+1)] end end
elseif config.endian == 'big' then
return quote for i = 0, bytes do dest[i] = src[i] end end
else error('unknown endianness '..config.endian) end
end)
-- swap out of place -- safer, more flexible, and optimized to an intrinsic call; trivial on big endian
m.netswap = macro(function(tyq, src)
if config.endian == 'little' then
local ty = tyq:astype()
local a,b = symbol(ty), symbol(ty)
local bytes = ty.bytes
local steps = {}
for i=0,bytes-1 do
steps[#steps + 1] = quote
b = b << 8
b = b or (a and 0xff)
a = a >> 8
end
end
return quote
var [a] = src
var [b] = 0
[steps]
in b end
elseif config.endian == 'big' then return `src
else error('unknown endianness '..config.endian) end
end)
terra m.shorthand.cval(character: int8): {uint8, bool}
var ch = [uint8](character)
if ch >= 0x30 and ch <= 0x39 then
ch = 00 + (ch - 0x30)
elseif ch == 0x2d then ch = 10
elseif ch >= 0x41 and ch <= 0x5a then
ch = 11 + (ch - 0x41)
elseif ch == 0x3a then ch = 37
elseif ch >= 0x61 and ch <= 0x7a then
ch = 38 + (ch - 0x61)
else return 0, false end
return ch, true
end
terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff
var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz"
var buf: int8[m.shorthand.maxlen]
var ptr = [&int8](buf)
while val ~= 0 do
var v = val % 64
@ptr = lst[v]
ptr = ptr + 1
val = val / 64
end
var len = ptr - buf
for i = 0, len do
dest[i] = buf[len - (i+1)]
end
dest[len] = 0
return len
end
terra m.shorthand.parse(s: rawstring, len: intptr): {uint64, bool}
var val: uint64 = 0
for i = 0, len do
var v, ok = m.shorthand.cval(s[i])
if ok == false then return 0, false end
val = (val * 64) + v
end
return val, true
end
terra m.truncate64(val: &uint8, len: intptr): uint64
var r: uint64 = 0
for i=0, len do
r = r << 3
r = r + @val
val = val + 1
end
return r
end
m.biggest = macro(function(a,b)
local ty = a.tree.type
if b.tree.type.bytes > ty.bytes then ty = b.tree.type end
return quote
var _a = [a]
var _b = [b]
var r: ty if _a > _b then r = _a else r = _b end
in r end
end)
m.smallest = macro(function(a,b)
local ty = a.tree.type
if b.tree.type.bytes < ty.bytes then ty = b.tree.type end
return quote
var _a = [a]
var _b = [b]
var r: ty if _a < _b then r = _a else r = _b end
in r end
end)
terra m.hexdigit(hb: uint8): int8
var a = hb and 0x0F
if a < 10 then return 0x30 + a
else return 0x61 + (a-10) end
end
terra m.hexbyte(b: uint8): int8[2]
return array(m.hexdigit((b and 0xF0) >> 4), m.hexdigit(b and 0x0F))
end
terra m.hexstr(src: &uint8, str: rawstring, sz: intptr)
for i = 0, sz do
var d = m.hexbyte(src[i])
str[i*2] = d[0]
str[i*2 + 1] = d[1]
end
end
terra m.b32char(v: uint8): int8
if v <= 25 then return 0x61 + v
elseif v < 31 then return 0x32 + (v-26)
else return 0 end
end
terra m.b32(v: uint64, buf: rawstring) -- 5 bytes -> 8 chars
while v > 0 do
var val = v % 32
v = v / 32
@buf = m.b32char(val)
buf = buf + 1
end
end
terra m.b32str(a: lib.mem.ptr(uint64))
end
terra m.decstr(val: intptr, buf: &int8): rawstring
-- works backwards to avoid copies. log10(2^64) ≈ 19.2 and we
-- need a byte for NUL so buf MUST point to THE END OF a buffer
-- at least 21 bytes long
@buf = 0
if val > 0 then while val > 0 do
buf = buf - 1
var dgt = val % 10
val = val / 10
@buf = 0x30 + dgt
end else
buf = buf - 1
@buf = 0x30
end
return buf
end
terra m.decstr_friendly(val: intptr, buf: &int8): rawstring
-- as above except needs size-28 buffers, on account of all the commas
@buf = 0
var dgtct: uint8 = 0
if val > 0 then while val > 0 do
buf = buf - 1
var dgt = val % 10
val = val / 10
@buf = 0x30 + dgt
if dgtct == 2 and val > 0 then
buf = buf - 1 @buf = @','
dgtct = 0
else dgtct = dgtct + 1 end
end else
buf = buf - 1
@buf = 0x30
end
return buf
end
terra m.decparse(s: pstring): {intptr, bool}
if not s then return 0, false end
var val:intptr = 0
var c = s.ptr
while @c ~= 0 do
if @c >= 0x30 and @c <= 0x39 then
val = val * 10
val = val + (@c - 0x30)
else
return 0, false
end
c = c + 1
if s.ct ~= 0 and (c - s.ptr > s.ct) then lib.dbg('reached end') return val, true end
end
return val, true
end
terra m.ndigits(n: intptr, base: intptr): intptr
var c = base
var i = 1
while true do
if n < c then return i end
c = c * base
i = i + 1
end
end
terra m.fsz_parse(f: pstring): {intptr, bool}
-- take a string representing a file size and return {nbytes, true}
-- or {0, false} if the parse fails
if f.ct == 0 then f.ct = lib.str.sz(f.ptr) end
var sz: intptr = 0
for i = 0, f.ct do
if f(i) == @',' then goto skip end
if f(i) >= 0x30 and f(i) <= 0x39 then
sz = sz * 10
sz = sz + f(i) - 0x30
else
if i+1 == f.ct or f(i) == 0 then return sz, true end
if i+2 == f.ct or f(i+1) == 0 then
if f(i) == @'b' then return sz/8, true end -- bits
else
var s: intptr = 0
if i+3 == f.ct or f(i+2) == 0 then
s = i + 1
elseif (i+4 == f.ct or f(i+3) == 0) and f(i+1) == @'i' then
-- grudgingly tolerate ~mebibits~ and its ilk, without
-- affecting the result in any way
s = i + 2
else return 0, false end
if f(s) == @'b' then sz = sz/8 -- bits
elseif f(s) ~= @'B' then return 0, false end -- wth
end
var c = f(i)
if c >= @'A' and c <= @'Z' then c = c - 0x20 end
switch c do -- normal char literal syntax doesn't work here, leads to llvm error (!!)
case [uint8]([string.byte('k')]) then return sz * [1024ULL ^ 1], true end
case [uint8]([string.byte('m')]) then return sz * [1024ULL ^ 2], true end
case [uint8]([string.byte('g')]) then return sz * [1024ULL ^ 3], true end
case [uint8]([string.byte('t')]) then return sz * [1024ULL ^ 4], true end
case [uint8]([string.byte('e')]) then return sz * [1024ULL ^ 5], true end
case [uint8]([string.byte('y')]) then return sz * [1024ULL ^ 6], true end
else return sz, true
end
end
::skip::end
return sz, true
end
return m