wow_cdbc/
lazy.rs

1//! Lazy loading functionality for DBC files
2
3use crate::{DbcHeader, Error, FieldType, Record, Result, Schema, StringBlock, Value};
4use std::io::{Cursor, Read};
5use std::sync::Arc;
6
7/// A record iterator that loads records on-demand
8pub struct LazyRecordIterator<'a> {
9    /// The cursor for reading records
10    cursor: Cursor<&'a [u8]>,
11    /// The DBC header
12    header: &'a DbcHeader,
13    /// The schema, if any
14    schema: Option<&'a Schema>,
15    /// The current record index
16    current_index: u32,
17    /// The total number of records
18    total_records: u32,
19}
20
21impl<'a> LazyRecordIterator<'a> {
22    /// Create a new lazy record iterator
23    pub fn new(
24        data: &'a [u8],
25        header: &'a DbcHeader,
26        schema: Option<&'a Schema>,
27        _string_block: Arc<StringBlock>,
28    ) -> Self {
29        let mut cursor = Cursor::new(data);
30        cursor.set_position(DbcHeader::SIZE as u64);
31
32        Self {
33            cursor,
34            header,
35            schema,
36            current_index: 0,
37            total_records: header.record_count,
38        }
39    }
40}
41
42impl Iterator for LazyRecordIterator<'_> {
43    type Item = Result<Record>;
44
45    fn next(&mut self) -> Option<Self::Item> {
46        if self.current_index >= self.total_records {
47            return None;
48        }
49
50        let record = if let Some(schema) = self.schema {
51            self.parse_record_with_schema(schema)
52        } else {
53            self.parse_record_raw()
54        };
55
56        self.current_index += 1;
57        Some(record)
58    }
59}
60
61impl LazyRecordIterator<'_> {
62    /// Parse a record using a schema
63    fn parse_record_with_schema(&mut self, schema: &Schema) -> Result<Record> {
64        let mut values = Vec::with_capacity(schema.fields.len());
65
66        for field in &schema.fields {
67            let value = if field.is_array {
68                let array_size = field.array_size.unwrap_or(0);
69                let mut array_values = Vec::with_capacity(array_size);
70
71                for _ in 0..array_size {
72                    array_values.push(self.parse_field_value(field.field_type)?);
73                }
74
75                Value::Array(array_values)
76            } else {
77                self.parse_field_value(field.field_type)?
78            };
79
80            values.push(value);
81        }
82
83        Ok(Record::new(values, Some(Arc::new(schema.clone()))))
84    }
85
86    /// Parse a record without a schema
87    fn parse_record_raw(&mut self) -> Result<Record> {
88        let mut values = Vec::with_capacity(self.header.field_count as usize);
89
90        for _ in 0..self.header.field_count {
91            // Without a schema, we assume all fields are 32-bit integers
92            let mut buf = [0u8; 4];
93            self.cursor.read_exact(&mut buf)?;
94            let value = u32::from_le_bytes(buf);
95            values.push(Value::UInt32(value));
96        }
97
98        Ok(Record::new(values, None))
99    }
100
101    /// Parse a field value based on its type
102    fn parse_field_value(&mut self, field_type: FieldType) -> Result<Value> {
103        crate::field_parser::parse_field_value(&mut self.cursor, field_type)
104    }
105}
106
107/// A lazy-loading DBC parser
108pub struct LazyDbcParser<'a> {
109    /// The DBC header
110    header: &'a DbcHeader,
111    /// The schema, if any
112    schema: Option<&'a Schema>,
113    /// The raw data of the DBC file
114    data: &'a [u8],
115    /// The string block
116    string_block: Arc<StringBlock>,
117}
118
119impl<'a> LazyDbcParser<'a> {
120    /// Create a new lazy DBC parser
121    pub fn new(
122        data: &'a [u8],
123        header: &'a DbcHeader,
124        schema: Option<&'a Schema>,
125        string_block: Arc<StringBlock>,
126    ) -> Self {
127        Self {
128            data,
129            header,
130            schema,
131            string_block,
132        }
133    }
134
135    /// Get a lazy record iterator
136    pub fn record_iterator(&self) -> LazyRecordIterator<'a> {
137        LazyRecordIterator::new(
138            self.data,
139            self.header,
140            self.schema,
141            Arc::clone(&self.string_block),
142        )
143    }
144
145    /// Get a record by index
146    pub fn get_record(&self, index: u32) -> Result<Record> {
147        if index >= self.header.record_count {
148            return Err(Error::OutOfBounds(format!(
149                "Record index out of bounds: {} (max: {})",
150                index,
151                self.header.record_count - 1
152            )));
153        }
154
155        let mut cursor = Cursor::new(self.data);
156        let record_position =
157            DbcHeader::SIZE as u64 + (index as u64 * self.header.record_size as u64);
158        cursor.set_position(record_position);
159
160        if let Some(schema) = self.schema {
161            self.parse_record_with_schema(&mut cursor, schema)
162        } else {
163            self.parse_record_raw(&mut cursor)
164        }
165    }
166
167    /// Parse a record using a schema
168    fn parse_record_with_schema(
169        &self,
170        cursor: &mut Cursor<&'a [u8]>,
171        schema: &Schema,
172    ) -> Result<Record> {
173        let mut values = Vec::with_capacity(schema.fields.len());
174
175        for field in &schema.fields {
176            let value = if field.is_array {
177                let array_size = field.array_size.unwrap_or(0);
178                let mut array_values = Vec::with_capacity(array_size);
179
180                for _ in 0..array_size {
181                    array_values.push(self.parse_field_value(cursor, field.field_type)?);
182                }
183
184                Value::Array(array_values)
185            } else {
186                self.parse_field_value(cursor, field.field_type)?
187            };
188
189            values.push(value);
190        }
191
192        Ok(Record::new(values, Some(Arc::new(schema.clone()))))
193    }
194
195    /// Parse a record without a schema
196    fn parse_record_raw(&self, cursor: &mut Cursor<&'a [u8]>) -> Result<Record> {
197        let mut values = Vec::with_capacity(self.header.field_count as usize);
198
199        for _ in 0..self.header.field_count {
200            // Without a schema, we assume all fields are 32-bit integers
201            let mut buf = [0u8; 4];
202            cursor.read_exact(&mut buf)?;
203            let value = u32::from_le_bytes(buf);
204            values.push(Value::UInt32(value));
205        }
206
207        Ok(Record::new(values, None))
208    }
209
210    /// Parse a field value based on its type
211    fn parse_field_value(
212        &self,
213        cursor: &mut Cursor<&'a [u8]>,
214        field_type: FieldType,
215    ) -> Result<Value> {
216        crate::field_parser::parse_field_value(cursor, field_type)
217    }
218
219    /// Get the DBC header
220    pub fn header(&self) -> &DbcHeader {
221        self.header
222    }
223
224    /// Get the schema, if any
225    pub fn schema(&self) -> Option<&Schema> {
226        self.schema
227    }
228
229    /// Get the string block
230    pub fn string_block(&self) -> &StringBlock {
231        &self.string_block
232    }
233}