rs_pcd/layout/
mod.rs

1// Copyright 2025 bigpear0201
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::error::{PcdError, Result};
16use crate::header::{PcdHeader, ValueType};
17
18#[derive(Debug, Clone, PartialEq)]
19pub struct FieldLayout {
20    pub name: String,
21    pub offset: usize,
22    pub size: usize,         // size in bytes of the field (type_size * count)
23    pub element_size: usize, // size of single element
24    pub count: usize,
25    pub type_: ValueType,
26}
27
28#[derive(Debug, Clone, PartialEq)]
29pub struct PcdLayout {
30    pub fields: Vec<FieldLayout>,
31    pub total_size: usize,
32}
33
34impl PcdLayout {
35    pub fn from_header(header: &PcdHeader) -> Result<Self> {
36        let mut fields = Vec::with_capacity(header.fields.len());
37        let mut offset = 0;
38
39        for (i, name) in header.fields.iter().enumerate() {
40            let type_char = header
41                .types
42                .get(i)
43                .ok_or_else(|| PcdError::LayoutMismatch {
44                    expected: header.fields.len(),
45                    got: i,
46                })?;
47
48            let size_in_header = *header
49                .sizes
50                .get(i)
51                .ok_or_else(|| PcdError::LayoutMismatch {
52                    expected: header.fields.len(),
53                    got: i,
54                })?;
55
56            let count = *header.counts.get(i).unwrap_or(&1);
57
58            let value_type = match type_char {
59                'I' => match size_in_header {
60                    1 => ValueType::I8,
61                    2 => ValueType::I16,
62                    4 => ValueType::I32,
63                    _ => return Err(PcdError::UnsupportedType(format!("I{}", size_in_header))),
64                },
65                'U' => match size_in_header {
66                    1 => ValueType::U8,
67                    2 => ValueType::U16,
68                    4 => ValueType::U32,
69                    _ => return Err(PcdError::UnsupportedType(format!("U{}", size_in_header))),
70                },
71                'F' => match size_in_header {
72                    4 => ValueType::F32,
73                    8 => ValueType::F64,
74                    _ => return Err(PcdError::UnsupportedType(format!("F{}", size_in_header))),
75                },
76                _ => return Err(PcdError::UnsupportedType(type_char.to_string())),
77            };
78
79            // Check if size * count matches expected logic if strict?
80            // PCD Header SIZE is size of *one* element typically (like '4' for float), even if count > 1.
81            // But let's verify. Yes, SIZE is per-element bytes.
82
83            let element_size = value_type.size();
84            if element_size != size_in_header {
85                return Err(PcdError::LayoutMismatch {
86                    expected: element_size,
87                    got: size_in_header,
88                });
89            }
90
91            let field_size = element_size * count;
92
93            fields.push(FieldLayout {
94                name: name.clone(),
95                offset,
96                size: field_size,
97                element_size,
98                count,
99                type_: value_type,
100            });
101
102            offset += field_size;
103        }
104
105        Ok(PcdLayout {
106            fields,
107            total_size: offset,
108        })
109    }
110
111    pub fn get_field(&self, name: &str) -> Option<&FieldLayout> {
112        self.fields.iter().find(|f| f.name == name)
113    }
114}