Convert a NTuple{N,UInt8} to a String and back?

What is the best practice for go back and forth between a NTuple{N,UInt8} where N and a String?

x = "Dfsddfs"
a = ntuple(i -> i <= ncodeunits(x) ? codeunit(x,i) : 0x0, 10 + 1)
function _ntuple_to_string(x)
    r = Ref(x)
    GC.@preserve r unsafe_string(Ptr{Cchar}(pointer_from_objref(r)))
end

julia> _ntuple_to_string(a)
"Dfsddfs"

Using unsafe_string like this seems pretty unsafe. (For one thing, you are assuming that the tuple-data is NUL-terminated because you didn’t pass a length. I’m surprised it doesn’t crash. Oh, I see, you explicitly NUL-terminated your tuple.)

I would do something like:

function ntuple_to_string(t::NTuple{N,UInt8}) where N
    b = Base.StringVector(N) # or N-1 if your tuples are NUL-terminated
    return String(b .= t) # or t[1:N-1]
end

We were thinking about Base.StringVector and the underlying jl_alloc_string but I noticed it’s still not exported or documented.

I also note that your implementation returns some extra null characters, so we might need to find the null byte and truncate the string.

julia> ntuple_to_string(a)
"Dfsddfs\0\0\0\0"

You didn’t specify that your NTuple was NUL-terminated. Yes, replace N with findfirst(iszero, t) - 1.

1 Like

It’s so widely used that it might as well be…

But you could alternatively use an ordinary Vector, at the price of making an extra copy of the data. Or use an ordinary Vector with StringViews.jl to avoid the copy.

1 Like

@stevengj I was feeling a bit unsatisfied with the state of this, so I decided to implement a NStrings.jl where I wrap an AbstractString around an NTuple{N,UInt8}, implementing your idea from why not an NTuple? · Issue #6 · JuliaStrings/InlineStrings.jl · GitHub . It incorporates your ntuple_to_string.