About.jl/src/types.jl

117 lines
3.9 KiB
Julia

const POINTER_FACE = :cyan # should not appear in `FACE_CYCLE`
struct FieldInfo
i::Int
face::Union{Symbol, Face}
offset::Int
size::Int
contentsize::Int
ispointer::Bool
name::Union{Symbol, Int}
type::Type
end
function structinfo(T::DataType)
map(1:fieldcount(T)) do i
size = Int(if i < fieldcount(T)
fieldoffset(T, i+1)
else
sizeof(T)
end - fieldoffset(T, i))
contentsize = try sizeof(fieldtype(T, i)) catch _ 0 end
if contentsize > size # Pointer?
contentsize = 0
end
FieldInfo(i, FACE_CYCLE[i % length(FACE_CYCLE) + 1],
fieldoffset(T, i) |> Int, # offset
size, contentsize,
contentsize == 0, # ispointer
fieldname(T, i), fieldtype(T, i))
end
end
function about(io::IO, type::Type)
if isprimitivetype(type)
print(io, "Primitive ")
elseif isconcretetype(type)
print(io, "Concrete ")
if Base.datatype_haspadding(type)
print(io, styled"{shadow:(padded)} ")
end
elseif isabstracttype(type)
print(io, "Abstract ")
end
if Base.issingletontype(type)
print(io, "singleton ")
end
print(Base.summary(type))
println(io, styled" defined in {bright_red:$(type.name.module)}, $(join(humansize(sizeof(type))))",
"\n ", supertypestr(type))
(!isstructtype(type) || fieldcount(type) == 0) && return
println(io, styled"\nStruct with {bold:$(fieldcount(type))} fields:")
fieldinfo = AnnotatedString[]
if type isa DataType
sinfo = structinfo(type)
namepad = maximum(fi -> textwidth(string(fi.name)), sinfo) + 1
for (; face, name, type, ispointer) in sinfo
push!(fieldinfo, rpad(styled"{$face:$name}", namepad) * styled"{$POINTER_FACE:$(ifelse(ispointer, \"*\", \" \"))}$type")
end
else
for (; name, type) in structinfo(type)
push!(fieldinfo, styled"$name{shadow:::$type}")
end
end
if length(fieldinfo) < 32
columnlist(io, fieldinfo, maxcols=1)
else
columnlist(io, fieldinfo, spacing=3)
end
if type isa DataType
println(io)
memorylayout(io, type)
end
end
supertypestr(type::Type) =
join(string.(supertypes(type)), styled" {red:<:} ")
function memorylayout(io::IO, type::DataType)
si = structinfo(type)
!isempty(si) || return
memstep = memstep = gcd((getfield.(si, :size), getfield.(si, :contentsize)) |>
Iterators.flatten |> collect)
memscale = max(1, floor(Int, 70/(sizeof(type)/memstep)))
bars = AnnotatedString[]
descs = AnnotatedString[]
for (; i, size, contentsize, ispointer) in si
color = FACE_CYCLE[i % length(FACE_CYCLE) + 1]
width = max(2, memscale * size÷memstep)
color = FACE_CYCLE[i % length(FACE_CYCLE) + 1]
fsize, funits = humansize(size)
desc = if ispointer
cpad(styled" {$color:*} ", width)
elseif contentsize < size
csize, cunits = humansize(contentsize)
psize, punits = humansize(size - contentsize)
cpad(styled" {$color:$csize$cunits}{shadow:+$psize$punits} ", width, ' ', RoundUp)
else
cpad(styled" {$color:$fsize$funits} ", width)
end
push!(descs, desc)
width = textwidth(desc)
contentwidth = round(Int, width * contentsize / size)
bar = styled"{$color:$('■'^contentwidth)}"
if contentsize < size
color = if ispointer; :cyan else :light_black end
paddwidth = width - contentwidth
bar *= styled"{$color:$('■'^paddwidth)}"
end
push!(bars, bar)
end
multirow_wrap(io, permutedims(hcat(bars, descs)))
if any(getfield.(si, :ispointer))
print(io, styled"\n {$POINTER_FACE:*} = {$POINTER_FACE:Pointer} {light:(8B)}")
end
println(io)
end