@@ -617,6 +617,28 @@ function asdf_datatype_yaml(dt::StructuredDatatype)
617617 end
618618end
619619
620+ """
621+ uses_float16(dt) -> Bool
622+
623+ Whether the datatype `dt` (including any nested field of a [`StructuredDatatype`](@ref))
624+ is or contains a `Float16`. The `float16` scalar datatype was only added to the ndarray
625+ schema in version 1.1.0, so such arrays must be tagged `!core/ndarray-1.1.0` rather than
626+ `!core/ndarray-1.0.0`. `complex32` is `Complex{Float16}`, so it counts too.
627+ """
628+ uses_float16 (dt:: Datatype ) = dt === Datatype_float16 || dt === Datatype_complex32
629+ uses_float16 (:: Union{AsciiDatatype, Ucs4Datatype} ) = false
630+ uses_float16 (dt:: StructuredDatatype ) = any (uses_float16 (f. datatype) for f in dt. fields)
631+
632+ """
633+ min_ndarray_version(datatype) -> VersionNumber
634+
635+ Lowest `!core/ndarray-X.Y.Z` schema version that can represent `datatype`: `float16` (and
636+ `complex32`, which contains it) require 1.1.0; every other datatype is valid from 1.0.0. This
637+ is the single source of truth shared by the reader (which rejects too-old tags) and the writer
638+ (which picks the tag version).
639+ """
640+ min_ndarray_version (datatype) = uses_float16 (datatype) ? v " 1.1.0" : v " 1.0.0"
641+
620642# ###############################################################################
621643
622644"""
@@ -781,7 +803,16 @@ function make_construct_yaml_ndarray(block_headers::LazyBlockHeaders)
781803 byteorder = get (mapping, " byteorder" , nothing ):: Union{Nothing,AbstractString}
782804 offset = get (mapping, " offset" , nothing ):: Union{Nothing,Integer}
783805 strides = get (mapping, " strides" , nothing ):: Union{Nothing,AbstractVector{<:Integer}}
784- return NDArray (block_headers, source, data, shape, datatype, byteorder, offset, strides)
806+ ndarray = NDArray (block_headers, source, data, shape, datatype, byteorder, offset, strides)
807+ # The datatype must be representable by the tag's ndarray schema version. `float16`
808+ # requires >= 1.1.0; `min_ndarray_version` is the single source of truth, shared with
809+ # the writer.
810+ required = min_ndarray_version (ndarray. datatype)
811+ m = match (r" ndarray-(\d +\.\d +\.\d +)$" , node. tag)
812+ if m != = nothing && VersionNumber (m[1 ]) < required
813+ error (" `float16` datatype requires `!core/ndarray-$required ` or newer, but tag is `$(node. tag) `" )
814+ end
815+ return ndarray
785816 end
786817 return construct_yaml_ndarray
787818end
@@ -1387,8 +1418,11 @@ function load_file(filename::AbstractString; extensions = false, validate_checks
13871418 construct_yaml_ndarray_chunk = make_construct_yaml_ndarray_chunk (lazy_block_headers)
13881419
13891420 asdf_constructors′ = copy (asdf_constructors)
1390- asdf_constructors′[" tag:stsci.edu:asdf/core/ndarray-1.0.0" ] = construct_yaml_ndarray
1391- asdf_constructors′[" tag:stsci.edu:asdf/core/ndarray-1.1.0" ] = construct_yaml_ndarray
1421+ # One constructor handles every ndarray schema version we accept; the per-datatype minimum
1422+ # is enforced inside it via `min_ndarray_version`.
1423+ for v in (v " 1.0.0" , v " 1.1.0" )
1424+ asdf_constructors′[" tag:stsci.edu:asdf/core/ndarray-$v " ] = construct_yaml_ndarray
1425+ end
13921426 asdf_constructors′[" tag:stsci.edu:asdf/core/ndarray-chunk-1.0.0" ] = construct_yaml_ndarray_chunk
13931427 asdf_constructors′[" tag:stsci.edu:asdf/core/chunked-ndarray-1.0.0" ] = construct_yaml_chunked_ndarray
13941428
@@ -1573,7 +1607,9 @@ function YAML._print(io::IO, val::NDArrayWrapper, level::Int=0, ignore_level::Bo
15731607 )
15741608 end
15751609 # println(io, YAML._indent("-\n", level), "!core/chunked-ndarray-1.0.0")
1576- println (io, " !core/ndarray-1.0.0" )
1610+ # Emit the lowest ndarray schema version that can represent this datatype (the reader's
1611+ # matching check in `make_construct_yaml_ndarray` uses the same `min_ndarray_version`).
1612+ println (io, " !core/ndarray-$(min_ndarray_version (datatype)) " )
15771613 YAML. _print (io, ndarray, level, ignore_level)
15781614end
15791615
0 commit comments