7070
7171@pure function packed_sizeof (T:: DataType , :: Type{Packed} )
7272 @assert fieldcount (T) != 0 && isbitstype (T)
73- return sum (packed_sizeof, T. types)
73+ return sum (packed_sizeof, T. types) + sum ( field_gaps (T))
7474end
7575
7676@pure function packed_sizeof (T:: DataType )
@@ -103,7 +103,6 @@ Return the size (in bytes) of a field within `T` in memory
103103 end
104104end
105105
106-
107106"""
108107 @io <type definition>
109108 ...
@@ -136,11 +135,20 @@ macro io(typ, annotations...)
136135 T = T. args[1 ]
137136 end
138137
138+ if alignment == :align_packed
139+ gap_values = strip_gap_nodes! (typ)
140+ end
141+
139142 ret = Expr (:toplevel , :(Base. @__doc__ $ (typ)))
140143 strat = (alignment == :align_default ? StructIO. Default : StructIO. Packed)
141144 push! (ret. args, :($ StructIO. packing_strategy (:: Type{T} ) where {T <: $T } = $ strat))
145+ if strat == Packed
146+ push! (ret. args, :($ StructIO. field_gaps (:: Type{T} ) where {T <: $T } = $ gap_values))
147+ end
148+
142149 return esc (ret)
143150end
151+ field_gaps (:: Type{T} ) where T = UInt[]
144152
145153"""
146154 unsafe_unpack(io, T, target, endianness, ::Type{Default})
@@ -242,7 +250,7 @@ function unsafe_pack(io, source::Ref{T}, endianness, ::Type{Default}) where {T}
242250end
243251
244252# `Packed` packing strategy override for `unsafe_unpack`
245- function unsafe_unpack (io, T, target, endianness, :: Type{Packed} )
253+ function unsafe_unpack (io, T, target, endianness, :: Type{Packed} ; gaps = field_gaps (T) )
246254 # If this type cannot be subdivided, packing strategy means nothing, so
247255 # hand it off to the `Default` packing strategy method
248256 if fieldcount (T) == 0
@@ -255,12 +263,15 @@ function unsafe_unpack(io, T, target, endianness, ::Type{Packed})
255263 # Unpack this field into `target` at the appropriate offset
256264 fT = fieldtype (T, i)
257265 target_i = target_ptr + fieldoffset (T, i)
266+ isempty (gaps) || skip (io, gaps[i])
258267 unsafe_unpack (io, fT, target_i, endianness, Packed)
259268 end
260269end
261270
262271# `Packed` packing strategy override for `unsafe_pack`
263- function unsafe_pack (io, source:: Ref{T} , endianness, :: Type{Packed} ) where {T}
272+ function unsafe_pack (
273+ io, source:: Ref{T} , endianness, :: Type{Packed} ; gaps= field_gaps (T)
274+ ) where {T}
264275 # If this type cannot be subdivided, packing strategy means nothing, so
265276 # hand it off to the `Default` packing strategy method
266277 if fieldcount (T) == 0
@@ -272,6 +283,7 @@ function unsafe_pack(io, source::Ref{T}, endianness, ::Type{Packed}) where {T}
272283 # Unpack this field into `target` at the appropriate offset
273284 fT = fieldtype (T, i)
274285 f = Ref {fT} (getfield (source[], fieldname (T, i)))
286+ isempty (gaps) || skip (io, gaps[i])
275287 unsafe_pack (io, f, endianness, Packed)
276288 end
277289end
@@ -311,4 +323,48 @@ function pack(io::IO, source::T, endianness::Symbol = :NativeEndian) where {T}
311323 return nothing
312324end
313325
326+ function strip_gap_nodes! (strct_expr:: Expr )
327+ @assert strct_expr. head == :struct
328+ strct_nodes = strct_expr. args[3 ]. args
329+ K = length (strct_nodes)
330+ gap_locations = UInt[]
331+ # accumulator, in case several gap specifications occur in unbroken succession
332+ gap_value = zero (UInt)
333+ # gaps before structure fields
334+ field_gap_values = UInt[]
335+ for k in 1 : K
336+ f = strct_nodes[k]
337+ ! isa (f, LineNumberNode) || continue
338+ if f. head === :(:: )
339+ # collect positions of gap nodes, when encountering a structure
340+ # field,
341+ if f. args[1 ] === :_
342+ if isa (f. args[2 ], Integer) && f. args[2 ] >= 0
343+ push! (gap_locations, k)
344+ gap_value += UInt (f. args[2 ])
345+ else
346+ error (" incorrect gap specification: $f " )
347+ end
348+ elseif isa (f. args[1 ], Symbol)
349+
350+ push! (field_gap_values, gap_value)
351+ gap_value = zero (UInt)
352+ else
353+ error (" unsupported structure field specificaion: $f " )
354+ end
355+ end
356+ end
357+ if all (iszero, field_gap_values)
358+ field_gap_values = UInt[]
359+ end
360+ # remove gap specifications from the expression tree
361+ for k in length (gap_locations): - 1 : 1
362+ deleteat! (strct_nodes, gap_locations[k])
363+ end
364+ strct_expr. args[3 ]. args = strct_nodes
365+
366+ return field_gap_values
367+ end
368+
369+
314370end # module
0 commit comments