sizeof(MVector{N, T}) where T <: costomStructure returns unexpected values

I’m working on building a custom binary file reader using StaticArrays. In the reader, I would like to use the custom structure (TestS2) that contains a MVector of another custom structure (TestS1).
To get the byte size of the structures, I also defined the custom sizeof function. This function returns just fine for TestS1 but TestS2. Does anyone help me to make it worked?

Please see the following code.

using StaticArrays

abstract type AbsBinType end

mutable struct TestS1 <: AbsBinType
	p1::MVector{2, UInt8}
	p2::Int32
	TestS1() = new()
end

mutable struct TestS2{N} <: AbsBinType
	q1::UInt8
	q2::MVector{N, TestS1}
	TestS2{N}() where N = new()
end

import Base: sizeof
sizeof(s::Type{T}) where T <: AbsBinType = sum(sizeof.(fieldtypes(s)))

sizeof(TestS1)
    6

sizeof(TestS2{3}) # this should return 1 + 6 * 3 = 19
    25

I think this is not directly related to this post but this is how I would like to use AbsBinType for binary file reading.

function read(io::IO, s::Type{T}) where T <: AbsBinType 
	N = fieldcount(s)
	readStruct = s()
	for n in 1:N
		setproperty!(readStruct, fieldnames(s)[n], read(io, fieldtypes(s)[n]))
	end
	return readStruct
end

I found this post. This is similar to what I would like to ask but they use immutable static vector with primitive types.

Check sizeof(MVector{3, TestS1}), it should have run in your sizeof(TestS2{3}) call.

Also don’t implement sizeof yourself, it’ll be done for your composite types automatically and it takes into account the byte alignment of your system. You’ll see if you rerun the session without your own methods.

PS You don’t need the where N in your struct definition, {N} in the struct definition is the where clause. See, this would work: abstract type A end; mutable struct subA{N} <: A end. I really don’t know why you were even allowed to write the where N, it does nothing, not even throw errors mutable struct subA2{N} <: A where VarDoesNotExist end.

Thank you for your feedback. Somehow I left where N in the struct definition from my code. I edited my post.

I checked the sizeof(MVector{3, TestS1}) did not use the sizeof I defined. That would be very helpful if you give me the answers to my questions listed below.

  1. What does the byte alignment of the system?
    If I overwrite the sizeof for my custom structure, does this influence the code performance or cause the crash of the program?
  2. Is it possible to define the a new function to compute the byte size of MVector{3, TestS1}?
    If so, I will be able to define different name functions to compute the sizes.

Of course not, you defined a sizeof method for subtypes of AbsBinType, so the MVector does not involve it.

The wikipedia article probably does a better job than me, but I’ll try to explain it fast. You’ve heard of 32-bit vs 64-bit architecture, those are word sizes that the machine does memory reads/writes most efficiently. So, types are often arranged to fit in full words by being padded to full words or smaller powers of 2 bytes (1, 2, 4) that add up to a full word. 6 bytes was not a power of 2, so it was padded to 8 bytes. The Julia compiler does this automatically.

You really don’t want to do it because it misleads you how much space the instance is taking up. TestS1’s data uses 6 bytes, but it is aligned to take up 8 at a time. You don’t want sizeof to give you the wrong numbers, that’s why it’s automatically defined when the struct is defined.

Thank you for your kind explanations. That’s really helpful to understand!

I defined functions, bytesize, instead of overwriting sizeof.

bytesize(T::DataType) = sizeof(T)
bytesize(::Type{T}) where T <: AbsBinType = sum(bytesize.(fieldtypes(T)))
bytesize(::Type{MVector{N, T}}) where {N, T <: AbsBinType} = bytesize(T) * N

bytesize(TestS1)
    6
bytesize(TestS2{3})
    19

This is working fine for my usage. Thank you for your kindness.