From c277bc376810b2c334dd83d885b66b44864f9013 Mon Sep 17 00:00:00 2001 From: Beforerr Date: Mon, 31 Aug 2026 17:34:40 +0900 Subject: [PATCH] refactor: consolidate record header access, drop dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - record_size/get_record_type/next_record_offset in one place; the Header struct and six hand-rolled copies of the chain-walk arithmetic are gone - find_vdr/keys walk the VDR chains through OffsetsIterator - delete flatten_field_types, the runtime-n read_be/read_be_i, and the collection check_record_type — all uncalled - julia_type no longer builds a StaticString type its only caller discards --- .github/workflows/CI.yml | 8 +-- ext/CommonDataFormatCommonDataModelExt.jl | 16 +---- src/CommonDataFormat.jl | 21 ++++++ src/dataset.jl | 84 ++++++++++++++--------- src/decompress.jl | 17 ++--- src/enums.jl | 11 +-- src/loading/attribute.jl | 5 +- src/loading/variable.jl | 55 --------------- src/parsing.jl | 25 ------- src/records/adr.jl | 4 +- src/records/aedr.jl | 3 +- src/records/ccr.jl | 7 +- src/records/records.jl | 25 ++----- src/records/vdr.jl | 15 ++-- src/types.jl | 3 +- src/variable.jl | 10 +-- 16 files changed, 106 insertions(+), 203 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 86b05e3..f123f87 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,7 +13,7 @@ concurrency: cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 60 permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created @@ -25,17 +25,13 @@ jobs: version: - '1' - 'lts' - - 'pre' os: - ubuntu-latest - arch: - - x64 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - uses: julia-actions/cache@v3 - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 diff --git a/ext/CommonDataFormatCommonDataModelExt.jl b/ext/CommonDataFormatCommonDataModelExt.jl index 3a4deee..487db74 100644 --- a/ext/CommonDataFormatCommonDataModelExt.jl +++ b/ext/CommonDataFormatCommonDataModelExt.jl @@ -15,10 +15,7 @@ const SymbolOrString = Union{Symbol, AbstractString} CDM.path(ds::CDFDataset) = CDF.filename(ds) CDM.varnames(ds::CDFDataset) = keys(ds) -function CDM.variable(ds::CDFDataset, name::SymbolOrString) - return CDF.variable(ds, String(name)) -end - +CDM.variable(ds::CDFDataset, name::SymbolOrString) = ds[String(name)] CDM.attribnames(ds::CDFDataset) = CDF.attribnames(ds) CDM.attrib(ds::CDFDataset, args...) = CDF.attrib(ds, args...) @@ -31,15 +28,8 @@ CDM.attrib(var::CDFVariable, args...) = CDF.attrib(var, args...) function CDM.dimnames(var::CDFVariable, i) N = ndims(var) @assert i <= N - key = if i == N - "DEPEND_0" - elseif i == 1 - "DEPEND_1" - elseif i == 2 - "DEPEND_2" - else - "DEPEND_$i" - end + # The record dimension is last in Julia but is DEPEND_0 in the ISTP conventions. + key = i == N ? "DEPEND_0" : "DEPEND_$i" return CDF.attrib(var, key)::Union{String, Nothing} end diff --git a/src/CommonDataFormat.jl b/src/CommonDataFormat.jl index 5e3aca7..e982346 100644 --- a/src/CommonDataFormat.jl +++ b/src/CommonDataFormat.jl @@ -14,6 +14,27 @@ export Epoch, Epoch16, TT2000 export CDF_EPOCH, CDF_EPOCH16, CDF_TIME_TT2000, CDF_CHAR, CDF_UCHAR export is_record_varying +@static if isdefined(Base, :OncePerProcess) + const decompressors = Base.OncePerProcess{Channel{Decompressor}}() do + n_ch = nthreads() + chnl = Channel{Decompressor}(n_ch) + foreach(i -> put!(chnl, Decompressor()), 1:n_ch) + return chnl + end +else + const _decompressors = Ref{Union{Channel{Decompressor},Nothing}}(nothing) + function decompressors() + if _decompressors[] === nothing + n_ch = nthreads() + chnl = Channel{Decompressor}(n_ch) + foreach(i -> put!(chnl, Decompressor()), 1:n_ch) + _decompressors[] = chnl + end + return _decompressors[] + end + __init__() = _decompressors[] = nothing +end + include("epochs.jl") include("enums.jl") include("types.jl") diff --git a/src/dataset.jl b/src/dataset.jl index 5ee1c80..e409129 100644 --- a/src/dataset.jl +++ b/src/dataset.jl @@ -77,44 +77,63 @@ function _vdr_at(cdf::CDFDataset{FST}, offset::Int) where {FST} rVDR{FST}(buffer, offset) end -function find_vdr(cdf::CDFDataset, var_name::String) - gdr = GDR(cdf) - RecordSizeType = recordsize_type(cdf) - buffer = cdf.buffer +# Name is the first variable-width field of a VDR; everything before it is fixed size. +vdr_name(buffer, offset, ::Type{FST}) where {FST} = readname(buffer, offset + 45 + 5 * sizeof(FST)) +# r-variables are chained first, then z-variables +vdr_heads(cdf::CDFDataset) = (GDR(cdf).rVDRhead, GDR(cdf).zVDRhead) + +function find_vdr(cdf::CDFDataset{FST}, var_name::String) where {FST} + buffer = parent(cdf) var_name_bytes = codeunits(var_name) - vdr_name_offset = 45 + 5 * sizeof(RecordSizeType) - for current_offset in (gdr.rVDRhead, gdr.zVDRhead) - while current_offset != 0 - if readname(buffer, current_offset + vdr_name_offset) == var_name_bytes - return _vdr_at(cdf, Int(current_offset)) - end - current_offset = read_be(buffer, current_offset + 5 + sizeof(RecordSizeType), RecordSizeType) - end + for head in vdr_heads(cdf), offset in OffsetsIterator{FST}(buffer, head) + vdr_name(buffer, offset, FST) == var_name_bytes && return _vdr_at(cdf, offset) end return nothing end -# Direct variable access via indexing -function Base.getindex(cdf::CDFDataset, var_name::String) - return variable(cdf, var_name) +function Base.getindex(cdf::CDFDataset, name::String) + vdr = find_vdr(cdf, name) + isnothing(vdr) && throw(KeyError(name)) + return _variable(cdf, name, vdr) end +# Branch over dimension count so each leaf builds dims tuple at compile time statically +function _variable(cdf, name, vdr) + M = num_record_dims(vdr, cdf) + return Base.Cartesian.@nif 12 d -> (M == d - 1) d -> ( + d == 12 ? throw(ArgumentError("variable has $M dimensions; the CDF format allows at most 10")) : + _variable(cdf, name, vdr, Val(d - 1)) + ) +end + +function _variable(cdf, name, vdr, ::Val{M}) where {M} + dims = (map(Int, record_sizes(vdr, cdf, Val(M)))..., Int(vdr.max_rec) + 1) + code = Int(vdr.data_type) + if code == CDF_CHAR || code == CDF_UCHAR # eltype depends on runtime num_elems + T = StaticString{Int(vdr.num_elems),UInt8} + return CDFVariable{T,M + 1,typeof(vdr),typeof(cdf)}(name, vdr, cdf, dims) + end + # Branch to static constructor per element type + return Base.Cartesian.@nif( + 16, + d -> code == CODE_TYPE_PAIRS[d][1], + d -> _construct(cdf, name, vdr, dims, CODE_TYPE_PAIRS[d][2]), + d -> throw(ArgumentError("unsupported CDF data type $code")) + ) +end + +@inline _construct(cdf, name, vdr, dims::NTuple{N,Int}, ::Type{T}) where {N,T} = + CDFVariable{T,N,typeof(vdr),typeof(cdf)}(name, vdr, cdf, dims) + Base.length(cdf::CDFDataset) = Int(GDR(cdf).NrVars + GDR(cdf).NzVars) -function Base.keys(cdf::CDFDataset) - RecordSizeType = recordsize_type(cdf) - gdr = cdf.gdr - source = parent(cdf) - varnames = Vector{String}(undef, gdr.NrVars + gdr.NzVars) +function Base.keys(cdf::CDFDataset{FST}) where {FST} + buffer = parent(cdf) + varnames = Vector{String}(undef, length(cdf)) i = 1 - vdr_name_offset = 45 + 5 * sizeof(RecordSizeType) - for current_offset in (gdr.rVDRhead, gdr.zVDRhead) - while current_offset != 0 - vdr_next = read_be(source, current_offset + 5 + sizeof(RecordSizeType), RecordSizeType) - varnames[i] = String(readname(source, current_offset + vdr_name_offset)) - i += 1 - current_offset = vdr_next - end + for head in vdr_heads(cdf), offset in OffsetsIterator{FST}(buffer, head) + varnames[i] = String(vdr_name(buffer, offset, FST)) + i += 1 end return varnames end @@ -122,19 +141,16 @@ end Base.haskey(cdf::CDFDataset, var_name::String) = !isnothing(find_vdr(cdf, var_name)) # Walk the rVDR then zVDR chain directly; state = (offset, still_in_r_chain) -function Base.iterate(cdf::CDFDataset, state = (Int(GDR(cdf).rVDRhead), true)) +function Base.iterate(cdf::CDFDataset{FST}, state = (Int(GDR(cdf).rVDRhead), true)) where {FST} offset, in_rchain = state if offset == 0 in_rchain || return nothing offset, in_rchain = Int(GDR(cdf).zVDRhead), false offset == 0 && return nothing end - RecordSizeType = recordsize_type(cdf) buffer = parent(cdf) - name = String(readname(buffer, offset + 45 + 5 * sizeof(RecordSizeType))) - var = _variable(cdf, name, _vdr_at(cdf, offset)) - next_offset = Int(read_be(buffer, offset + 5 + sizeof(RecordSizeType), RecordSizeType)) - return (var, (next_offset, in_rchain)) + var = _variable(cdf, String(vdr_name(buffer, offset, FST)), _vdr_at(cdf, offset)) + return (var, (next_record_offset(buffer, offset, FST), in_rchain)) end function Base.show(io::IO, m::MIME"text/plain", cdf::CDFDataset) diff --git a/src/decompress.jl b/src/decompress.jl index 20d3a9e..e0fc1e9 100644 --- a/src/decompress.jl +++ b/src/decompress.jl @@ -9,32 +9,27 @@ function decompress_bytes(buffer, ::Type{RecordSizeType}) where {RecordSizeType} payload = data_view(ccr, buffer) expected = Int(ccr.uncompressed_size) decompressed = decompress_bytes(payload, compression; expected_bytes = expected) - new_size = 8 + length(decompressed) - new_buffer = Vector{UInt8}(undef, new_size) + new_buffer = Vector{UInt8}(undef, 8 + length(decompressed)) copyto!(new_buffer, 1, buffer, 1, 4) - new_buffer[5] = 0x00 - new_buffer[6] = 0x00 - new_buffer[7] = 0xFF - new_buffer[8] = 0xFF + write_be(new_buffer, 5, 0x0000FFFF) # the uncompressed marker replaces the CCR magic copyto!(new_buffer, 9, decompressed, 1, length(decompressed)) return new_buffer, compression end function decompress_bytes(data, compression::CompressionType; expected_bytes::Union{Nothing, Int} = nothing) compression == NoCompression && return data - compression in (GzipCompression, RLECompression) || - throw(ArgumentError("unsupported compression: $compression")) result = if compression == GzipCompression - decompressor = Decompressor() input = convert(Vector{UInt8}, data) max_size = isnothing(expected_bytes) ? length(input) * 10 : expected_bytes output = Vector{UInt8}(undef, max_size) - decomp_result = gzip_decompress!(decompressor, output, input) + decomp_result = gzip_decompress!(Decompressor(), output, input) resize!(output, decomp_result.len) output - else + elseif compression == RLECompression isnothing(expected_bytes) && throw(ArgumentError("RLE decompression requires expected size")) _rle_decompress(data, expected_bytes) + else + throw(ArgumentError("unsupported compression: $compression")) end if !isnothing(expected_bytes) && length(result) != expected_bytes throw(ArgumentError("Decompressed payload size mismatch (expected $(expected_bytes), got $(length(result)))")) diff --git a/src/enums.jl b/src/enums.jl index 8951e7b..0608a62 100644 --- a/src/enums.jl +++ b/src/enums.jl @@ -53,13 +53,6 @@ const CODE_TYPE_PAIRS = ( (11, UInt8), (12, UInt16), (14, UInt32), (41, Int8), ) -const type_map = Dict{CDFDataType, Type}( - Dict(CDFDataType(c) => T for (c, T) in CODE_TYPE_PAIRS)..., - CDF_CHAR => UInt8, - CDF_UCHAR => UInt8, -) +const type_map = Dict{CDFDataType, Type}(CDFDataType(c) => T for (c, T) in CODE_TYPE_PAIRS) -function julia_type(cdf_type, num_elems) - cdf_type = CDFDataType(cdf_type) - return cdf_type in (CDF_CHAR, CDF_UCHAR) ? StaticString{Int(num_elems), UInt8} : type_map[cdf_type] -end +julia_type(cdf_type) = type_map[CDFDataType(cdf_type)] diff --git a/src/loading/attribute.jl b/src/loading/attribute.jl index d410137..bd449e4 100644 --- a/src/loading/attribute.jl +++ b/src/loading/attribute.jl @@ -59,7 +59,7 @@ function Base.iterate(la::LazyVAttrib, offset::Int = Int(la.cdf.gdr.ADRhead)) while offset != 0 # cheap scope check before parsing the full ADR (avoids Name string allocation for globals) if is_global(buffer, offset, RecordSizeType) - offset = Int(read_be(buffer, offset + 1 + sizeof(RecordSizeType) + 4, RecordSizeType)) + offset = next_record_offset(buffer, offset, RecordSizeType) continue end adr = ADR{RecordSizeType}(buffer, offset) @@ -125,13 +125,12 @@ end aedr_head == 0 && return nothing offset = Int(aedr_head) _num_offset = 13 + 2 * sizeof(RecordSizeType) - _next_offset = 5 + sizeof(RecordSizeType) while offset != 0 num = read_be(source, offset + _num_offset, Int32) if num == target_varnum return load_aedr_data(source, offset, RecordSizeType, needs_byte_swap) end - offset = Int(read_be(source, offset + _next_offset, RecordSizeType)) + offset = next_record_offset(source, offset, RecordSizeType) end return nothing end diff --git a/src/loading/variable.jl b/src/loading/variable.jl index d50ab89..f2fe9a0 100644 --- a/src/loading/variable.jl +++ b/src/loading/variable.jl @@ -1,24 +1,3 @@ -@static if isdefined(Base, :OncePerProcess) - const decompressors = Base.OncePerProcess{Channel{Decompressor}}() do - n_ch = nthreads() - chnl = Channel{Decompressor}(n_ch) - foreach(i -> put!(chnl, Decompressor()), 1:n_ch) - return chnl - end -else - const _decompressors = Ref{Union{Channel{Decompressor},Nothing}}(nothing) - function decompressors() - if _decompressors[] === nothing - n_ch = nthreads() - chnl = Channel{Decompressor}(n_ch) - foreach(i -> put!(chnl, Decompressor()), 1:n_ch) - _decompressors[] = chnl - end - return _decompressors[] - end - __init__() = _decompressors[] = nothing -end - # ---- Type-erased block moves: dims/strides are runtime vectors ---- # Element movers, chosen once per block by element size; `ByteMove` covers odd sizes (CHAR data). struct WordMove{U} end @@ -115,40 +94,6 @@ function _copy_subblock!(dst::Ptr{UInt8}, src::Ptr{UInt8}, esz::Int, rdims::Vect return end -function variable(cdf::CDFDataset, name) - vdr = find_vdr(cdf, name) - isnothing(vdr) && throw(KeyError(name)) - return _variable(cdf, name, vdr) -end - -# Branch over dimension count so each leaf builds dims tuple at compile time statically -function _variable(cdf, name, vdr) - M = num_record_dims(vdr, cdf) - return Base.Cartesian.@nif 12 d -> (M == d - 1) d -> ( - d == 12 ? throw(ArgumentError("variable has $M dimensions; the CDF format allows at most 10")) : - _variable(cdf, name, vdr, Val(d - 1)) - ) -end - -function _variable(cdf, name, vdr, ::Val{M}) where {M} - dims = (map(Int, record_sizes(vdr, cdf, Val(M)))..., Int(vdr.max_rec) + 1) - code = Int(vdr.data_type) - if code == 51 || code == 52 # CHAR/UCHAR: eltype depends on runtime num_elems - T = StaticString{Int(vdr.num_elems),UInt8} - return CDFVariable{T,M + 1,typeof(vdr),typeof(cdf)}(name, vdr, cdf, dims) - end - # Branch to static constructor per element type - return Base.Cartesian.@nif( - 16, - d -> code == CODE_TYPE_PAIRS[d][1], - d -> _construct(cdf, name, vdr, dims, CODE_TYPE_PAIRS[d][2]), - d -> throw(ArgumentError("unsupported CDF data type $code")) - ) -end - -@inline _construct(cdf, name, vdr, dims::NTuple{N,Int}, ::Type{T}) where {N,T} = - CDFVariable{T,N,typeof(vdr),typeof(cdf)}(name, vdr, cdf, dims) - function DiskArrays.readblock!(var::CDFVariable{T,N}, dest::AbstractArray{T}, ranges::Vararg{AbstractUnitRange{<:Integer},N}) where {T,N} N > 0 && @boundscheck checkbounds(var, ranges...) isempty(dest) && return dest diff --git a/src/parsing.jl b/src/parsing.jl index b21aaae..cef79f8 100644 --- a/src/parsing.jl +++ b/src/parsing.jl @@ -16,11 +16,6 @@ end return ntoh(unsafe_load(p + (i - 1) * sizeof(T))) end -@inline function read_be(v::Vector{UInt8}, i, n, T) - S = sizeof(T) - return ntuple(j -> read_be(v, i + (j - 1) * S, T), n) -end - @inline function read_be(v::Vector{UInt8}, i, ::Val{M}, T) where {M} S = sizeof(T) return ntuple(j -> read_be(v, i + (j - 1) * S, T), Val(M)) @@ -30,11 +25,6 @@ end return read_be(v, i, T), i + _sizeof(T) end -@inline function read_be_i(v::Vector{UInt8}, i, n::Integer, T) - S = sizeof(T) - return ntuple(j -> read_be(v, i + (j - 1) * S, T), n), i + n * S -end - function field_layout(SType, indxs) offsets = Int[] types = Any[] @@ -69,21 +59,6 @@ end @inline write_be(v::Vector{UInt8}, i, ::RInt32) = i + _sizeof(RInt32) -function flatten_field_types(mod, args) - types = Any[] - for arg in args - if arg isa Expr && arg.head === :... - vals = Base.eval(mod, arg.args[1]) - for T in vals - push!(types, Meta.quot(T)) - end - else - push!(types, arg) - end - end - return types -end - function readname(buf::Vector{UInt8}, offset::Int) for i in offset:(offset + 255) if buf[i] == 0x00 diff --git a/src/records/adr.jl b/src/records/adr.jl index 61c7f74..ca4b9ce 100644 --- a/src/records/adr.jl +++ b/src/records/adr.jl @@ -20,8 +20,8 @@ struct ADR{FSZ, S} <: Record end is_global(adr) = adr.Scope == 1 -is_global(buffer, offset, ::Type{Int32}) = read_be(buffer, offset + 17, Int32) == 1 -is_global(buffer, offset, ::Type{Int64}) = read_be(buffer, offset + 29, Int32) == 1 +# Scope, read without parsing the rest of the ADR: header + ADRnext + AgrEDRhead +is_global(buffer, offset, ::Type{FST}) where {FST} = read_be(buffer, offset + 3 * sizeof(FST) + 5, Int32) == 1 @inline function ADR{FST}(buffer::Vector{UInt8}, offset) where {FST} diff --git a/src/records/aedr.jl b/src/records/aedr.jl index 86b4024..7800d27 100644 --- a/src/records/aedr.jl +++ b/src/records/aedr.jl @@ -27,11 +27,10 @@ function load_aedr_data(buffer::Vector{UInt8}, offset, ::Type{RecordSizeType}, n _data_offset = 41 + 2 * sizeof(RecordSizeType) datatype = read_be(buffer, offset + _datatype_offset, Int32) NumElems = read_be(buffer, offset + _numelems_offset, Int32) - T = julia_type(datatype, NumElems) return if datatype in (CDF_CHAR, CDF_UCHAR) load_char_data(buffer, offset + _data_offset, NumElems) else - load_attribute_data(T, buffer, offset + _data_offset, NumElems, needs_byte_swap) + load_attribute_data(julia_type(datatype), buffer, offset + _data_offset, NumElems, needs_byte_swap) end end diff --git a/src/records/ccr.jl b/src/records/ccr.jl index e20a7c3..83e7be1 100644 --- a/src/records/ccr.jl +++ b/src/records/ccr.jl @@ -7,14 +7,11 @@ struct CCR <: Record end @inline function CCR(buffer::Vector{UInt8}, offset, ::Type{RecordSizeType}) where {RecordSizeType} - pos = offset + 1 - header = Header(buffer, pos, RecordSizeType) - @assert header.record_type == 10 "Invalid CCR record type" - pos += sizeof(RecordSizeType) + 4 + record_end = offset + record_size(buffer, offset, RecordSizeType) + pos = check_record_type(10, buffer, offset, RecordSizeType) cpr_offset, pos = read_be_i(buffer, pos, RecordSizeType) uncompressed_size, pos = read_be_i(buffer, pos, RecordSizeType) rfu_a, data_offset = read_be_i(buffer, pos, RInt32) - record_end = offset + header.record_size data_length = record_end - data_offset @assert data_length >= 0 "Invalid CCR data length" return CCR(UInt64(cpr_offset), UInt64(uncompressed_size), rfu_a, data_offset, data_length) diff --git a/src/records/records.jl b/src/records/records.jl index 7e7629c..ec36fde 100644 --- a/src/records/records.jl +++ b/src/records/records.jl @@ -3,21 +3,11 @@ # [CDF Internal Format Description](https://spdf.gsfc.nasa.gov/pub/software/cdf/doc/cdfifd.pdf) Base.iterate(r::Record, i = 1) = i > fieldcount(typeof(r)) ? nothing : (getfield(r, i), i + 1) -""" -CDF Record header structure - common to all record types -""" -struct Header - record_size::Int64 # Can be Int32 for v2, Int64 for v3 - record_type::Int32 -end - -@inline function Header(buf::Vector{UInt8}, pos, ::Type{FieldSizeT}) where {FieldSizeT} - record_size = Int64(read_be(buf, pos, FieldSizeT)) - record_type = read_be(buf, pos + sizeof(FieldSizeT), Int32) - return Header(record_size, record_type) -end - +# Every record starts with its size (FieldSizeT) then its type (Int32). The ADR, AEDR and +# VDR chains all store the next record's offset in the field right after that header. +record_size(buffer, offset, ::Type{FieldSizeT}) where {FieldSizeT} = Int(read_be(buffer, offset + 1, FieldSizeT)) get_record_type(buffer, offset, ::Type{FieldSizeT}) where {FieldSizeT} = read_be(buffer, offset + sizeof(FieldSizeT) + 1, Int32) +next_record_offset(buffer, offset, ::Type{FieldSizeT}) where {FieldSizeT} = Int(read_be(buffer, offset + sizeof(FieldSizeT) + 5, FieldSizeT)) @inline function check_record_type(record_type::Integer, buffer, offset, ::Type{FieldSizeT}) where {FieldSizeT} pos = offset + sizeof(FieldSizeT) + 1 @@ -26,13 +16,6 @@ get_record_type(buffer, offset, ::Type{FieldSizeT}) where {FieldSizeT} = read_be return pos + sizeof(Int32) end -@inline function check_record_type(record_types, buffer, offset, ::Type{FieldSizeT}) where {FieldSizeT} - pos = offset + sizeof(FieldSizeT) + 1 - header_type = read_be(buffer, pos, Int32) - @assert header_type in record_types - return pos + sizeof(Int32) -end - include("cdr.jl") include("gdr.jl") include("vdr.jl") diff --git a/src/records/vdr.jl b/src/records/vdr.jl index 9a98e3b..09d7ce7 100644 --- a/src/records/vdr.jl +++ b/src/records/vdr.jl @@ -47,6 +47,9 @@ struct VDR{FST} <: AbstractVDR{FST} # dim_varys::Tuple{Vararg{Int32}} # Dimension variance flags end +# zNumDims (zVDRs) / DimVarys (rVDRs) start right after the fixed-width name field. +_after_name(offset, ::Type{FieldSizeT}) where {FieldSizeT} = FieldSizeT == Int64 ? offset + 341 : offset + 129 + """ VDR{FieldSizeT}(buffer, offset) @@ -54,11 +57,8 @@ Load a z-Variable Descriptor Record from the buffer at the specified offset. """ @inline function VDR{FieldSizeT}(buffer::Vector{UInt8}, offset) where {FieldSizeT} pos = check_record_type(8, buffer, offset, FieldSizeT) - fields, pos = read_be_fields(buffer, pos, VDR{FieldSizeT}, Val(1:13)) - # name = readname(buffer, pos) - - pos = FieldSizeT == Int64 ? offset + 340 + 1 : offset + 128 + 1 - z_num_dims, pos = read_be_i(buffer, pos, Int32) + fields, _ = read_be_fields(buffer, pos, VDR{FieldSizeT}, Val(1:13)) + z_num_dims, pos = read_be_i(buffer, _after_name(offset, FieldSizeT), Int32) return VDR{FieldSizeT}(fields..., z_num_dims, pos) end @@ -69,9 +69,8 @@ Load an r-Variable Descriptor Record from the buffer at the specified offset. """ @inline function rVDR{FieldSizeT}(buffer::Vector{UInt8}, offset) where {FieldSizeT} pos = check_record_type(3, buffer, offset, FieldSizeT) - fields, pos = read_be_fields(buffer, pos, rVDR{FieldSizeT}, Val(1:13)) - pos = FieldSizeT == Int64 ? offset + 340 + 1 : offset + 128 + 1 - return rVDR{FieldSizeT}(fields..., pos) + fields, _ = read_be_fields(buffer, pos, rVDR{FieldSizeT}, Val(1:13)) + return rVDR{FieldSizeT}(fields..., _after_name(offset, FieldSizeT)) end function record_sizes(vdr::VDR, cdf, ::Val{M}) where {M} diff --git a/src/types.jl b/src/types.jl index a7e1472..42a1588 100644 --- a/src/types.jl +++ b/src/types.jl @@ -16,6 +16,5 @@ Base.eltype(::Type{<:OffsetsIterator}) = Int function Base.iterate(iter::OffsetsIterator{RecordSizeType}, pos::Int = iter.start_pos) where {RecordSizeType} pos == 0 && return nothing - next_pos = Int(read_be(iter.buffer, pos + 1 + sizeof(RecordSizeType) + 4, RecordSizeType)) - return (pos, next_pos) + return (pos, next_record_offset(iter.buffer, pos, RecordSizeType)) end diff --git a/src/variable.jl b/src/variable.jl index 3bc1b7f..aef587a 100644 --- a/src/variable.jl +++ b/src/variable.jl @@ -56,13 +56,9 @@ end @inline function Base.getproperty(var::CDFVariable, name::Symbol) name in fieldnames(CDFVariable) && return getfield(var, name) - if name == :attrib - return attrib(var) - elseif name == :datatype - return CDFDataType(var.vdr.data_type) - else - throw(ArgumentError("Unknown property $name")) - end + name === :attrib && return attrib(var) + name === :datatype && return CDFDataType(var.vdr.data_type) + throw(ArgumentError("Unknown property $name")) end Base.getindex(var::CDFVariable, name::String) = var.attrib[name]