Skip to main content

netcdf_reader/classic/
mod.rs

1//! Classic (CDF-1/2/5) NetCDF file format support.
2//!
3//! This module handles the original NetCDF binary format (CDF-1 classic, CDF-2
4//! 64-bit offset, and CDF-5 64-bit data). All multi-byte values are big-endian.
5
6pub mod data;
7pub mod header;
8pub mod types;
9pub mod variable;
10
11use std::fs::File;
12use std::path::Path;
13
14use memmap2::Mmap;
15
16use crate::error::Result;
17use crate::types::NcGroup;
18use crate::NcFormat;
19
20/// Backing storage for a classic NetCDF file.
21pub(crate) enum ClassicData {
22    Mmap(Mmap),
23    Bytes(Vec<u8>),
24}
25
26impl ClassicData {
27    pub fn as_slice(&self) -> &[u8] {
28        match self {
29            ClassicData::Mmap(m) => m,
30            ClassicData::Bytes(b) => b,
31        }
32    }
33}
34
35/// An opened classic-format NetCDF file (CDF-1, CDF-2, or CDF-5).
36pub struct ClassicFile {
37    pub(crate) format: NcFormat,
38    pub(crate) root_group: NcGroup,
39    pub(crate) data: ClassicData,
40    pub(crate) numrecs: u64,
41}
42
43impl ClassicFile {
44    /// Open a classic NetCDF file from disk using memory-mapping.
45    pub fn open(path: &Path, format: NcFormat) -> Result<Self> {
46        let file = File::open(path)?;
47        // SAFETY: read-only mapping; caller must not modify the file concurrently.
48        let mmap = unsafe { Mmap::map(&file)? };
49        let header = header::parse_header(&mmap, format)?;
50
51        let root_group = NcGroup {
52            name: "/".to_string(),
53            dimensions: header.dimensions,
54            variables: header.variables,
55            attributes: header.global_attributes,
56            groups: Vec::new(), // Classic format has no sub-groups.
57        };
58
59        Ok(ClassicFile {
60            format,
61            root_group,
62            data: ClassicData::Mmap(mmap),
63            numrecs: header.numrecs,
64        })
65    }
66
67    /// Open a classic NetCDF file from in-memory bytes.
68    pub fn from_bytes(bytes: &[u8], format: NcFormat) -> Result<Self> {
69        let header = header::parse_header(bytes, format)?;
70
71        let root_group = NcGroup {
72            name: "/".to_string(),
73            dimensions: header.dimensions,
74            variables: header.variables,
75            attributes: header.global_attributes,
76            groups: Vec::new(),
77        };
78
79        Ok(ClassicFile {
80            format,
81            root_group,
82            data: ClassicData::Bytes(bytes.to_vec()),
83            numrecs: header.numrecs,
84        })
85    }
86
87    /// Open a classic NetCDF file from an existing memory map (avoids double mmap).
88    pub fn from_mmap(mmap: Mmap, format: NcFormat) -> Result<Self> {
89        let header = header::parse_header(&mmap, format)?;
90
91        let root_group = NcGroup {
92            name: "/".to_string(),
93            dimensions: header.dimensions,
94            variables: header.variables,
95            attributes: header.global_attributes,
96            groups: Vec::new(),
97        };
98
99        Ok(ClassicFile {
100            format,
101            root_group,
102            data: ClassicData::Mmap(mmap),
103            numrecs: header.numrecs,
104        })
105    }
106
107    /// The file format (Classic, Offset64, or Cdf5).
108    pub fn format(&self) -> NcFormat {
109        self.format
110    }
111
112    /// The root group containing all dimensions, variables, and global attributes.
113    pub fn root_group(&self) -> &NcGroup {
114        &self.root_group
115    }
116
117    /// Number of records in the unlimited dimension.
118    pub fn numrecs(&self) -> u64 {
119        self.numrecs
120    }
121}