avoid allocations during serialization

- count number of bytes before writing nested structs

- rewrite serialize functions to take an output range