Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Changed

- **Breaking**: `CDF_CHAR`/`CDF_UCHAR` variables have eltype `String` (trailing null padding stripped); `StaticString` is removed. Avoids a recompile for each new string width ([#79]).

## [0.3.0] - 2026-10-02

### Changed
Expand Down Expand Up @@ -29,5 +33,6 @@
[0.3.0]: https://github.com/JuliaSpacePhysics/CommonDataFormat.jl/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/JuliaSpacePhysics/CommonDataFormat.jl/releases/tag/v0.2.0

[#79]: https://github.com/JuliaSpacePhysics/CommonDataFormat.jl/pull/79
[#74]: https://github.com/JuliaSpacePhysics/CommonDataFormat.jl/pull/74
[#46]: https://github.com/JuliaSpacePhysics/CommonDataFormat.jl/pull/46
1 change: 0 additions & 1 deletion src/CommonDataFormat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ end
include("epochs.jl")
include("enums.jl")
include("types.jl")
include("staticstring.jl")
include("parsing.jl")
include("decompress.jl")
include("records/records.jl")
Expand Down
6 changes: 1 addition & 5 deletions src/dataset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,9 @@ 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,
18,
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"))
Expand Down
1 change: 1 addition & 0 deletions src/enums.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const CODE_TYPE_PAIRS = (
(33, TT2000), (31, Epoch), (32, Epoch16),
(1, Int8), (2, Int16), (4, Int32), (8, Int64),
(11, UInt8), (12, UInt16), (14, UInt32), (41, Int8),
(51, String), (52, String),
)

const type_map = Dict{CDFDataType, Type}(CDFDataType(c) => T for (c, T) in CODE_TYPE_PAIRS)
Expand Down
37 changes: 34 additions & 3 deletions src/loading/variable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,43 @@ function DiskArrays.readblock!(var::CDFVariable{T,N}, dest::AbstractArray{T}, ra
copyto!(dest, DiskArrays.readblock!(var, Array{T}(undef, size(dest)), ranges...))
return dest
end
GC.@preserve dest _readblock!(Ptr{UInt8}(_ptr(dest)), sizeof(T), _block_args(var, ranges)..., var.vdr, var.parentdataset)
is_big_endian_encoding(var) && _byte_swap!(dest)
return dest
end

# CHAR elements are `num_elems`-byte fields, null-padded; the string ends at the trailing-null run.
function DiskArrays.readblock!(var::CDFVariable{String,N}, dest::AbstractArray{String}, ranges::Vararg{AbstractUnitRange{<:Integer},N}) where {N}
N > 0 && @boundscheck checkbounds(var, ranges...)
isempty(dest) && return dest
if !(dest isa Array)
copyto!(dest, DiskArrays.readblock!(var, Array{String}(undef, size(dest)), ranges...))
return dest
end
w = Int(var.vdr.num_elems)
bytes = Vector{UInt8}(undef, w * length(dest))
GC.@preserve bytes _readblock!(pointer(bytes), w, _block_args(var, ranges)..., var.vdr, var.parentdataset)
_fill_strings!(vec(dest), bytes, w)
return dest
end

function _fill_strings!(dest::Vector{String}, bytes::Vector{UInt8}, w::Int)
GC.@preserve bytes for k in eachindex(dest)
o = (k - 1) * w
n = w
while n > 0 && iszero(@inbounds bytes[o + n])
n -= 1
end
dest[k] = unsafe_string(pointer(bytes, o + 1), n)
end
return dest
end

function _block_args(var::CDFVariable, ranges)
rdims = Int[Base.front(var.dims)...]
other_ranges = UnitRange{Int}[Int(first(r)):Int(last(r)) for r in Base.front(ranges)]
rec_range = Int(first(ranges[end])):Int(last(ranges[end]))
GC.@preserve dest _readblock!(Ptr{UInt8}(_ptr(dest)), sizeof(T), rdims, other_ranges, rec_range, var.vdr, var.parentdataset)
is_big_endian_encoding(var) && _byte_swap!(dest)
return dest
return rdims, other_ranges, rec_range
end

function _readblock!(dest::Ptr{UInt8}, esz::Int, rdims::Vector{Int}, other_ranges::Vector{UnitRange{Int}}, rec_range::UnitRange{Int}, vdr, ds)
Expand Down
1 change: 0 additions & 1 deletion src/parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ end

# Big-endian files: swap each `unit`-byte word in place (0 = raw bytes, no swap).
_swap_unit(::Type{T}) where {T} = sizeof(T)
_swap_unit(::Type{<:StaticString}) = 0
_swap_unit(::Type{Epoch16}) = 8 # two Float64s
function _byte_swap!(p::Ptr{UInt8}, nbytes::Int, unit::Int)
unit <= 1 && return
Expand Down
6 changes: 3 additions & 3 deletions src/precompile.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Seed one variable per common (eltype, ndims) shape, per CDF v2/v3 offset width. Every new combination costs DiskArrays indexing compiles, so rarer
# shapes (integers, Epoch16, >3-D, extra string widths) are left out.
_workload_key(var) = (eltype(var) <: StaticString ? StaticString : eltype(var), ndims(var), recordsize_type(var.parentdataset))
_workload_maxdims(T) = T <: AbstractFloat ? 3 : T <: StaticString ? 2 : T in (TT2000, Epoch) ? 1 : 0
# shapes (integers, Epoch16, >3-D) are left out.
_workload_key(var) = (eltype(var), ndims(var), recordsize_type(var.parentdataset))
_workload_maxdims(T) = T <: AbstractFloat ? 3 : T === String ? 2 : T in (TT2000, Epoch) ? 1 : 0

PrecompileTools.@setup_workload begin
files = joinpath.(@__DIR__, "../data", ("elb_l2_epdef_20210914_v01.cdf", "a_cdf_with_compressed_vars.cdf", "ac_h2_sis_20101105_v06.cdf"))
Expand Down
95 changes: 0 additions & 95 deletions src/staticstring.jl

This file was deleted.

6 changes: 2 additions & 4 deletions test/comprehensive_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,8 @@ ds = CDFDataset(file)
@test Set(keys(ds)) == Set(keys(EXPECTED_VARIABLES))
end

@testset "StaticString" begin
using CommonDataFormat: StaticString
@test typeof(ds["var_string"][1]) == StaticString{16, UInt8}
@test String(ds["var_string"][1]) == "This is a string"
@testset "CHAR" begin
@test ds["var_string"][1] == "This is a string"
end

@testset "DateTime Conversions" begin
Expand Down
5 changes: 5 additions & 0 deletions test/parsing_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ end
@test Dict(la)["VAR_TYPE"] == "metadata"
@test length(ds.vattrib["FIELDNAM"]) == length(ds)
end

@testset "CHAR null padding" begin
bytes = UInt8['a', 'b', 0, 0, 'c', 0, 'd', 0]
@test CommonDataFormat._fill_strings!(Vector{String}(undef, 2), bytes, 4) == ["ab", "c\0d"]
end
3 changes: 0 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ include("cdf2_test.jl")
include("CommonDataModelExt_test.jl")
include("decompress_test.jl")
include("parsing_test.jl")
@testset "StaticString" begin
include("staticstring.jl")
end

@testset "Aqua" begin
using Aqua
Expand Down
51 changes: 0 additions & 51 deletions test/staticstring.jl

This file was deleted.

Loading