! SPDX-Identifer: MIT #:include "common.fypp" #:set RANKS = range(1, MAXRANK + 1) #:set KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES + CMPLX_KINDS_TYPES !> Description of the npy format taken from !> https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html !> !>## Format Version 1.0 !> !> The first 6 bytes are a magic string: exactly \x93NUMPY. !> !> The next 1 byte is an unsigned byte: !> the major version number of the file format, e.g. \x01. !> !> The next 1 byte is an unsigned byte: !> the minor version number of the file format, e.g. \x00. !> Note: the version of the file format is not tied to the version of the numpy package. !> !> The next 2 bytes form a little-endian unsigned short int: !> the length of the header data HEADER_LEN. !> !> The next HEADER_LEN bytes form the header data describing the array’s format. !> It is an ASCII string which contains a Python literal expression of a dictionary. !> It is terminated by a newline (\n) and padded with spaces (\x20) to make the total !> of len(magic string) + 2 + len(length) + HEADER_LEN be evenly divisible by 64 for !> alignment purposes. !> !> The dictionary contains three keys: !> !> - “descr”: dtype.descr !> An object that can be passed as an argument to the numpy.dtype constructor !> to create the array’s dtype. !> !> - “fortran_order”: bool !> Whether the array data is Fortran-contiguous or not. Since Fortran-contiguous !> arrays are a common form of non-C-contiguity, we allow them to be written directly !> to disk for efficiency. !> !> - “shape”: tuple of int !> The shape of the array. !> !> For repeatability and readability, the dictionary keys are sorted in alphabetic order. !> This is for convenience only. A writer SHOULD implement this if possible. A reader MUST !> NOT depend on this. !> !> Following the header comes the array data. If the dtype contains Python objects !> (i.e. dtype.hasobject is True), then the data is a Python pickle of the array. !> Otherwise the data is the contiguous (either C- or Fortran-, depending on fortran_order) !> bytes of the array. Consumers can figure out the number of bytes by multiplying the !> number of elements given by the shape (noting that shape=() means there is 1 element) !> by dtype.itemsize. !> !>## Format Version 2.0 !> !> The version 1.0 format only allowed the array header to have a total size of 65535 bytes. !> This can be exceeded by structured arrays with a large number of columns. !> The version 2.0 format extends the header size to 4 GiB. numpy.save will automatically !> save in 2.0 format if the data requires it, else it will always use the more compatible !> 1.0 format. !> !> The description of the fourth element of the header therefore has become: !> “The next 4 bytes form a little-endian unsigned int: the length of the header data !> HEADER_LEN.” !> !>## Format Version 3.0 !> !> This version replaces the ASCII string (which in practice was latin1) with a !> utf8-encoded string, so supports structured types with any unicode field names. module stdlib_io_npy use stdlib_kinds, only : int8, int16, int32, int64, sp, dp, xdp, qp implicit none private public :: save_npy, load_npy !> Version: experimental !> !> Save multidimensional array in npy format !> ([Specification](../page/specs/stdlib_io.html#save_npy)) interface save_npy #:for k1, t1 in KINDS_TYPES #:for rank in RANKS module subroutine save_npy_${t1[0]}$${k1}$_${rank}$(filename, array, iostat, iomsg) character(len=*), intent(in) :: filename ${t1}$, intent(in) :: array${ranksuffix(rank)}$ integer, intent(out), optional :: iostat character(len=:), allocatable, intent(out), optional :: iomsg end subroutine save_npy_${t1[0]}$${k1}$_${rank}$ #:endfor #:endfor end interface save_npy !> Version: experimental !> !> Load multidimensional array in npy format !> ([Specification](../page/specs/stdlib_io.html#load_npy)) interface load_npy #:for k1, t1 in KINDS_TYPES #:for rank in RANKS module subroutine load_npy_${t1[0]}$${k1}$_${rank}$(filename, array, iostat, iomsg) character(len=*), intent(in) :: filename ${t1}$, allocatable, intent(out) :: array${ranksuffix(rank)}$ integer, intent(out), optional :: iostat character(len=:), allocatable, intent(out), optional :: iomsg end subroutine load_npy_${t1[0]}$${k1}$_${rank}$ #:endfor #:endfor end interface load_npy character(len=*), parameter :: nl = achar(10) character(len=*), parameter :: & type_iint8 = "<i1", type_iint16 = "<i2", type_iint32 = "<i4", type_iint64 = "<i8", & type_rsp = "<f4", type_rdp = "<f8", type_rxdp = "<f10", type_rqp = "<f16", & type_csp = "<c8", type_cdp = "<c16", type_cxdp = "<c20", type_cqp = "<c32" character(len=*), parameter :: & & magic_number = char(int(z"93")), & & magic_string = "NUMPY" end module stdlib_io_npy