Can I get a Slice
pointing at a single UInt8
without creating a new Bytes
or StaticArray
?
Bytes
is defined as just alias Bytes = Slice(UInt8)
. With this in mind your question could be rephrased to “Can I get a Slice
… without creating a new Slice
…?” :)
So the answer is obviously no, just do Bytes[10u8]
.
However maybe for your usecase you can avoid allocating the slice by not using a slice altogether? Always good to mention the underlying problem for which you seek a solution, rather than how to realize a solution you already came up with :)
This might be what you want:
x = 0u8
slice = pointerof(x).to_slice(1)
slice[0] = 1u8
p slice # => Bytes[1]
It doesn’t allocate anything on the heap.
I really want to avoid heap allocation and Bytes.new
.
Bytes[]
calls T.slice
# The slice is allocated on the heap.
macro slice(*nums, read_only = false)
Well you got your solution above :) Maybe there’s a safe and more idiomatic one though, one that doesn’t even need a slice…
How would you handle passing portions of c structs to IO
methods, Digest#update
and other stdlib methods that only take a Slice
?
For IO
there’s the write_bytes
interface which is supported by all primitive types in stdlib, through the to_io
interface defined on them. One could expand on that by defining to_io
methods for compound types.
If something is type restricted to a Slice
argument with no alternative API, of course you need to get your hands onto a slice somehow :)
In general I still think providing the context on where you need something is still beneficial to the question quality and allows to offer solutions outside of the path you might have imagined so far. That’s all I was hinting at :)
@asterite Porting existing code from ruby/c.
@exilor’s answer seems to work but I haven’t validated the hash values yet.
slice = pointerof(struct.member).to_slice(1)
digest.update slice
Memory allocations using Bytes.new(1)
were killing performance. I could keep a persistent Bytes
allocated but a Slice
directly to the UInt8
seems like a better option.
Using pointerof is fine as long as you don’t give that pointer to something that keeps a reference to it.
Also see this: https://github.com/crystal-lang/crystal/blob/2cbe65b0a13c8a2ce91bd4799f5b96b8e1953097/src/io.cr#L842-L845
def write_byte(byte : UInt8)
x = byte
write Slice.new(pointerof(x), 1)
end
I’d also point out that StaticArray
doesn’t allocate on the heap, so it might actually be a viable, more high-level solution where you don’t need to work with pointers directly.
In the process of making a PR adding write(byte : UInt8)
to Digest
I wondered if it made more sense to add to_slice
to UInt8
?
Thoughts?
Isn’t that what DigestIO
is for? So it gets methods like IO#write_byte
inherited for free?
I don’t think so. DigestIO calculates a Digest
of the unmodified stream as you read/write an IO
. I don’t have an IO
and if it was it’s not the entire stream or in order.
I wondered if it made more sense to add
to_slice
toUInt8
?
The comment above is about adding easy UInt8
compatibility for every function that accepts Bytes
which is more than IO
.
I think it’s fair if doing this is a bit hard, IO#write_byte(s)
is just so much more sane of an interface and we should push people towards it where possible. I don’t want people to end up with io.write 1u8.to_slice
.
I think write
takes anything that responds to #to_slice
as does Digest#update
and many more.
What I propose:
io.write 1_u8
digest.update 2_u8
cipher.update 3_u8
any_duck_typed_to_slice_accepting_method 4_u8
If it’s not done at the UInt8
level, code must be repeated for every function that wants to accept a Slice | Byte
Maybe DigestIO
is a misnomer and Should be DigestIOWrapper
or something less terrible, and then Digest
should just include IO
and disable read
?
We have this wonderful to_io
interface, it would indeed be a shame to not use it.
Maybe Digest
should even use a builder API akin to String.build
, yielding an IO
. Having to call final
on it always felt like an odd API to me.
There’s write_byte
for IO. I think having it for Digest is good too.
UInt8#to_slice
will never happen because it’s unsafe. If you retain the slice and pass it around, it might crash your program or cause undefined behavior.