Skip to main content

outlook_pst/ltp/
table_context.rs

1//! ## [Table Context (TC)](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/5e48be0d-a75a-4918-a277-50408ff96740)
2
3use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
4use std::{
5    cell::RefCell,
6    collections::BTreeMap,
7    fmt::Debug,
8    io::{self, Cursor, Read, Write},
9    marker::PhantomData,
10    rc::Rc,
11};
12
13use super::{heap::*, prop_context::*, prop_type::*, read_write::*, tree::*, *};
14use crate::{
15    messaging::{
16        read_write::StoreReadWrite,
17        store::{AnsiStore, UnicodeStore},
18    },
19    ndb::{
20        block::{Block, DataBlockCache, DataTree, IntermediateTreeBlock, SubNodeTree},
21        block_id::BlockId,
22        block_ref::BlockRef,
23        header::Header,
24        node_id::{NodeId, NodeIdType},
25        page::{
26            AnsiNodeBTreeEntry, BlockBTreeEntry, NodeBTreeEntry, RootBTree, UnicodeNodeBTreeEntry,
27        },
28        read_write::*,
29        root::Root,
30    },
31    AnsiPstFile, PstFile, PstFileLock, UnicodePstFile,
32};
33
34pub const LTP_ROW_ID_PROP_ID: u16 = 0x67F2;
35pub const LTP_ROW_VERSION_PROP_ID: u16 = 0x67F3;
36
37pub const fn existence_bitmap_size(column_count: usize) -> usize {
38    column_count / 8 + if column_count % 8 == 0 { 0 } else { 1 }
39}
40
41pub const fn check_existence_bitmap(column: usize, existence_bitmap: &[u8]) -> LtpResult<bool> {
42    if column >= existence_bitmap.len() * 8 {
43        return Err(LtpError::InvalidTableContextColumnCount(column));
44    }
45    Ok(existence_bitmap[column / 8] & (1_u8 << (7 - (column % 8))) != 0)
46}
47
48/// [TCINFO](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/45b3a0c5-d6d6-4e02-aebf-13766ff693f0)
49#[derive(Clone, Default, Debug)]
50pub struct TableContextInfo {
51    end_4byte_values: u16,
52    end_2byte_values: u16,
53    end_1byte_values: u16,
54    end_existence_bitmap: u16,
55    row_index: HeapId,
56    rows: Option<NodeId>,
57    _deprecated_index: u32,
58    columns: Vec<TableColumnDescriptor>,
59}
60
61impl TableContextInfo {
62    pub fn new(
63        end_4byte_values: u16,
64        end_2byte_values: u16,
65        end_1byte_values: u16,
66        end_existence_bitmap: u16,
67        row_index: HeapId,
68        rows: Option<NodeId>,
69        columns: Vec<TableColumnDescriptor>,
70    ) -> LtpResult<Self> {
71        if columns.len() > 0xFF {
72            return Err(LtpError::InvalidTableContextColumnCount(columns.len()));
73        }
74
75        if end_4byte_values % 4 != 0 {
76            return Err(LtpError::InvalidTableContext4ByteOffset(end_4byte_values));
77        }
78
79        if end_2byte_values % 2 != 0 || end_2byte_values < end_4byte_values {
80            return Err(LtpError::InvalidTableContext2ByteOffset(end_2byte_values));
81        }
82
83        if end_1byte_values < end_2byte_values {
84            return Err(LtpError::InvalidTableContext1ByteOffset(end_1byte_values));
85        }
86
87        if end_existence_bitmap < end_1byte_values
88            || (end_existence_bitmap - end_1byte_values) as usize
89                != existence_bitmap_size(columns.len())
90        {
91            return Err(LtpError::InvalidTableContextBitmaskOffset(
92                end_existence_bitmap,
93            ));
94        }
95
96        for column in columns.iter() {
97            match (column.prop_type(), column.prop_id()) {
98                (PropertyType::Integer32, LTP_ROW_ID_PROP_ID) => {
99                    match (column.offset(), column.existence_bitmap_index()) {
100                        (0, 0) => {}
101                        _ => {
102                            return Err(LtpError::InvalidTableContextRowIdColumn(
103                                column.prop_id(),
104                                column.prop_type(),
105                            ));
106                        }
107                    }
108                }
109                (PropertyType::Integer32, LTP_ROW_VERSION_PROP_ID) => {
110                    match (column.offset(), column.existence_bitmap_index()) {
111                        (4, 1) => {}
112                        _ => {
113                            return Err(LtpError::InvalidTableContextRowIdColumn(
114                                column.prop_id(),
115                                column.prop_type(),
116                            ));
117                        }
118                    }
119                }
120                _ => {}
121            }
122
123            match column.prop_type() {
124                PropertyType::Integer16
125                | PropertyType::Integer32
126                | PropertyType::Floating32
127                | PropertyType::Floating64
128                | PropertyType::Currency
129                | PropertyType::FloatingTime
130                | PropertyType::ErrorCode
131                | PropertyType::Boolean
132                | PropertyType::Integer64
133                | PropertyType::String8
134                | PropertyType::Unicode
135                | PropertyType::Time
136                | PropertyType::Guid
137                | PropertyType::Binary
138                | PropertyType::Object
139                | PropertyType::MultipleInteger16
140                | PropertyType::MultipleInteger32
141                | PropertyType::MultipleFloating32
142                | PropertyType::MultipleFloating64
143                | PropertyType::MultipleCurrency
144                | PropertyType::MultipleFloatingTime
145                | PropertyType::MultipleInteger64
146                | PropertyType::MultipleString8
147                | PropertyType::MultipleUnicode
148                | PropertyType::MultipleTime
149                | PropertyType::MultipleGuid
150                | PropertyType::MultipleBinary => {}
151                prop_type => {
152                    return Err(LtpError::InvalidTableColumnPropertyType(prop_type));
153                }
154            }
155
156            match (column.prop_type(), column.offset()) {
157                (PropertyType::Boolean, offset)
158                    if offset >= end_2byte_values && offset < end_1byte_values => {}
159                (PropertyType::Integer16, offset)
160                    if offset % 2 == 0
161                        && offset >= end_4byte_values
162                        && offset + 2 <= end_2byte_values => {}
163                (
164                    PropertyType::Integer32
165                    | PropertyType::Floating32
166                    | PropertyType::ErrorCode
167                    | PropertyType::String8
168                    | PropertyType::Unicode
169                    | PropertyType::Guid
170                    | PropertyType::Binary
171                    | PropertyType::Object
172                    | PropertyType::MultipleInteger16
173                    | PropertyType::MultipleInteger32
174                    | PropertyType::MultipleFloating32
175                    | PropertyType::MultipleFloating64
176                    | PropertyType::MultipleCurrency
177                    | PropertyType::MultipleFloatingTime
178                    | PropertyType::MultipleInteger64
179                    | PropertyType::MultipleString8
180                    | PropertyType::MultipleUnicode
181                    | PropertyType::MultipleTime
182                    | PropertyType::MultipleGuid
183                    | PropertyType::MultipleBinary,
184                    offset,
185                ) if offset % 4 == 0 && offset + 4 <= end_4byte_values => {}
186                (
187                    PropertyType::Floating64
188                    | PropertyType::Currency
189                    | PropertyType::FloatingTime
190                    | PropertyType::Integer64
191                    | PropertyType::Time,
192                    offset,
193                ) if offset % 4 == 0 && offset + 8 <= end_4byte_values => {}
194                (_, offset) => {
195                    return Err(LtpError::InvalidTableColumnOffset(offset));
196                }
197            }
198
199            match (column.prop_type(), column.size()) {
200                (PropertyType::Boolean, 1) => {}
201                (PropertyType::Integer16, 2) => {}
202                (
203                    PropertyType::Integer32
204                    | PropertyType::Floating32
205                    | PropertyType::ErrorCode
206                    | PropertyType::String8
207                    | PropertyType::Unicode
208                    | PropertyType::Guid
209                    | PropertyType::Binary
210                    | PropertyType::Object
211                    | PropertyType::MultipleInteger16
212                    | PropertyType::MultipleInteger32
213                    | PropertyType::MultipleFloating32
214                    | PropertyType::MultipleFloating64
215                    | PropertyType::MultipleCurrency
216                    | PropertyType::MultipleFloatingTime
217                    | PropertyType::MultipleInteger64
218                    | PropertyType::MultipleString8
219                    | PropertyType::MultipleUnicode
220                    | PropertyType::MultipleTime
221                    | PropertyType::MultipleGuid
222                    | PropertyType::MultipleBinary,
223                    4,
224                ) => {}
225                (
226                    PropertyType::Floating64
227                    | PropertyType::Currency
228                    | PropertyType::FloatingTime
229                    | PropertyType::Integer64
230                    | PropertyType::Time,
231                    8,
232                ) => {}
233                (_, size) => {
234                    return Err(LtpError::InvalidTableColumnSize(size));
235                }
236            }
237
238            if usize::from(column.existence_bitmap_index()) > columns.len() {
239                return Err(LtpError::InvalidTableColumnBitmaskOffset(
240                    column.existence_bitmap_index(),
241                ));
242            }
243        }
244
245        Ok(Self {
246            end_4byte_values,
247            end_2byte_values,
248            end_1byte_values,
249            end_existence_bitmap,
250            row_index,
251            rows,
252            _deprecated_index: 0,
253            columns,
254        })
255    }
256
257    pub fn end_4byte_values(&self) -> u16 {
258        self.end_4byte_values
259    }
260
261    pub fn end_2byte_values(&self) -> u16 {
262        self.end_2byte_values
263    }
264
265    pub fn end_1byte_values(&self) -> u16 {
266        self.end_1byte_values
267    }
268
269    pub fn end_existence_bitmap(&self) -> u16 {
270        self.end_existence_bitmap
271    }
272
273    pub fn columns(&self) -> &[TableColumnDescriptor] {
274        &self.columns
275    }
276}
277
278impl TableContextInfoReadWrite for TableContextInfo {
279    fn read(f: &mut dyn Read) -> io::Result<Self> {
280        // bType
281        let signature = HeapNodeType::try_from(f.read_u8()?)?;
282        if signature != HeapNodeType::Table {
283            return Err(LtpError::InvalidTableContextHeapTreeNodeType(signature).into());
284        }
285
286        // cCols
287        let column_count = f.read_u8()?;
288
289        // rgib
290        let end_4byte_values = f.read_u16::<LittleEndian>()?;
291        let end_2byte_values = f.read_u16::<LittleEndian>()?;
292        let end_1byte_values = f.read_u16::<LittleEndian>()?;
293        let end_existence_bitmap = f.read_u16::<LittleEndian>()?;
294
295        // hidRowIndex
296        let row_index = HeapId::read(f)?;
297
298        // hnidRows
299        let rows = NodeId::read(f)?;
300        let rows = if u32::from(rows) == 0 {
301            None
302        } else {
303            Some(rows)
304        };
305
306        // hidIndex
307        let _deprecated_index = f.read_u32::<LittleEndian>()?;
308
309        // rgTCOLDESC
310        let mut columns = Vec::with_capacity(usize::from(column_count));
311        for _ in 0..column_count {
312            columns.push(TableColumnDescriptor::read(f)?);
313        }
314
315        Ok(Self {
316            _deprecated_index,
317            ..Self::new(
318                end_4byte_values,
319                end_2byte_values,
320                end_1byte_values,
321                end_existence_bitmap,
322                row_index,
323                rows,
324                columns,
325            )?
326        })
327    }
328
329    fn write(&self, f: &mut dyn Write) -> io::Result<()> {
330        if self.columns.len() > 0xFF {
331            return Err(LtpError::InvalidTableContextColumnCount(self.columns.len()).into());
332        }
333
334        // bType
335        f.write_u8(HeapNodeType::Table as u8)?;
336
337        // cCols
338        f.write_u8(self.columns.len() as u8)?;
339
340        // rgib
341        f.write_u16::<LittleEndian>(self.end_4byte_values)?;
342        f.write_u16::<LittleEndian>(self.end_2byte_values)?;
343        f.write_u16::<LittleEndian>(self.end_1byte_values)?;
344        f.write_u16::<LittleEndian>(self.end_existence_bitmap)?;
345
346        // hidRowIndex
347        self.row_index.write(f)?;
348
349        // hnidRows
350        self.rows.unwrap_or_default().write(f)?;
351
352        // hidIndex
353        f.write_u32::<LittleEndian>(self._deprecated_index)?;
354
355        // rgTCOLDESC
356        for column in &self.columns {
357            column.write(f)?;
358        }
359
360        Ok(())
361    }
362}
363
364/// [TCOLDESC](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/3a2f63cf-bb40-4559-910c-e55ec43d9cbb)
365#[derive(Clone, Copy, Default, Debug)]
366pub struct TableColumnDescriptor {
367    prop_type: PropertyType,
368    prop_id: u16,
369    offset: u16,
370    size: u8,
371    existence_bitmap_index: u8,
372}
373
374impl TableColumnDescriptor {
375    pub fn new(
376        prop_type: PropertyType,
377        prop_id: u16,
378        offset: u16,
379        size: u8,
380        existence_bitmap_index: u8,
381    ) -> Self {
382        Self {
383            prop_type,
384            prop_id,
385            offset,
386            size,
387            existence_bitmap_index,
388        }
389    }
390
391    pub fn prop_type(&self) -> PropertyType {
392        self.prop_type
393    }
394
395    pub fn prop_id(&self) -> u16 {
396        self.prop_id
397    }
398
399    pub fn offset(&self) -> u16 {
400        self.offset
401    }
402
403    pub fn size(&self) -> u8 {
404        self.size
405    }
406
407    pub fn existence_bitmap_index(&self) -> u8 {
408        self.existence_bitmap_index
409    }
410}
411
412impl TableColumnDescriptorReadWrite for TableColumnDescriptor {
413    fn read(f: &mut dyn Read) -> io::Result<Self> {
414        let prop_type = PropertyType::try_from(f.read_u16::<LittleEndian>()?)?;
415        let prop_id = f.read_u16::<LittleEndian>()?;
416        let offset = f.read_u16::<LittleEndian>()?;
417        let size = f.read_u8()?;
418        let existence_bitmap_index = f.read_u8()?;
419
420        Ok(Self {
421            prop_type,
422            prop_id,
423            offset,
424            size,
425            existence_bitmap_index,
426        })
427    }
428
429    fn write(&self, f: &mut dyn Write) -> io::Result<()> {
430        f.write_u16::<LittleEndian>(self.prop_type as u16)?;
431        f.write_u16::<LittleEndian>(self.prop_id)?;
432        f.write_u16::<LittleEndian>(self.offset)?;
433        f.write_u8(self.size)?;
434        f.write_u8(self.existence_bitmap_index)?;
435
436        Ok(())
437    }
438}
439
440#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
441pub struct TableRowId {
442    id: u32,
443}
444
445impl TableRowId {
446    pub fn new(id: u32) -> Self {
447        Self { id }
448    }
449}
450
451impl From<TableRowId> for u32 {
452    fn from(value: TableRowId) -> Self {
453        value.id
454    }
455}
456
457impl HeapTreeEntryKey for TableRowId {
458    const SIZE: u8 = 4;
459}
460
461impl HeapNodePageReadWrite for TableRowId {
462    fn read(f: &mut dyn Read) -> io::Result<Self> {
463        let id = f.read_u32::<LittleEndian>()?;
464        Ok(Self { id })
465    }
466
467    fn write(&self, f: &mut dyn Write) -> io::Result<()> {
468        f.write_u32::<LittleEndian>(self.id)
469    }
470}
471
472trait TableRowIndex<Pst>: HeapTreeEntryValue + HeapNodePageReadWrite + Copy
473where
474    Pst: PstFile,
475    u32: From<Self>,
476{
477    type Index: Copy;
478}
479
480#[derive(Clone, Copy, Default, Debug)]
481pub struct UnicodeTableRowIndex {
482    index: u32,
483}
484
485impl TableRowIndex<UnicodePstFile> for UnicodeTableRowIndex {
486    type Index = u32;
487}
488
489impl From<UnicodeTableRowIndex> for u32 {
490    fn from(value: UnicodeTableRowIndex) -> Self {
491        value.index
492    }
493}
494
495impl HeapTreeEntryValue for UnicodeTableRowIndex {
496    const SIZE: u8 = 4;
497}
498
499impl HeapNodePageReadWrite for UnicodeTableRowIndex {
500    fn read(f: &mut dyn Read) -> io::Result<Self> {
501        let index = f.read_u32::<LittleEndian>()?;
502        Ok(Self { index })
503    }
504
505    fn write(&self, f: &mut dyn Write) -> io::Result<()> {
506        f.write_u32::<LittleEndian>(self.index)
507    }
508}
509
510#[derive(Clone, Copy, Default, Debug)]
511pub struct AnsiTableRowIndex {
512    index: u16,
513}
514
515impl TableRowIndex<AnsiPstFile> for AnsiTableRowIndex {
516    type Index = u16;
517}
518
519impl From<AnsiTableRowIndex> for u32 {
520    fn from(value: AnsiTableRowIndex) -> Self {
521        u32::from(value.index)
522    }
523}
524
525impl HeapTreeEntryValue for AnsiTableRowIndex {
526    const SIZE: u8 = 2;
527}
528
529impl HeapNodePageReadWrite for AnsiTableRowIndex {
530    fn read(f: &mut dyn Read) -> io::Result<Self> {
531        let index = f.read_u16::<LittleEndian>()?;
532        Ok(Self { index })
533    }
534
535    fn write(&self, f: &mut dyn Write) -> io::Result<()> {
536        f.write_u16::<LittleEndian>(self.index)
537    }
538}
539
540trait TableRowIndexTree<Pst>: HeapTreeReadWrite<Pst, Key = TableRowId, Value = Self::RowIndex>
541where
542    Pst: PstFile,
543    u32: From<Self::RowIndex>,
544{
545    type RowIndex: TableRowIndex<Pst>;
546}
547
548/// [TCROWID](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/e20b5cf4-ea56-48b8-a8fa-e086c9b862ca)
549pub type UnicodeTableRowIdRecord = HeapTreeLeafEntry<TableRowId, UnicodeTableRowIndex>;
550
551/// [TCROWID](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/e20b5cf4-ea56-48b8-a8fa-e086c9b862ca)
552pub type AnsiTableRowIdRecord = HeapTreeLeafEntry<TableRowId, AnsiTableRowIndex>;
553
554#[derive(Clone, Debug)]
555pub enum TableRowColumnValue {
556    Small(PropertyValue),
557    Heap(HeapId),
558    Node(NodeId),
559}
560
561/// [Row Data Format](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-pst/c48fa6b4-bfd4-49d7-80f8-8718bc4bcddc)
562pub struct TableRowData {
563    id: TableRowId,
564    unique: u32,
565    align_4byte: Vec<u8>,
566    align_2byte: Vec<u8>,
567    align_1byte: Vec<u8>,
568    existence_bitmap: Vec<u8>,
569}
570
571impl TableRowData {
572    pub fn new(
573        id: TableRowId,
574        unique: u32,
575        align_4byte: Vec<u8>,
576        align_2byte: Vec<u8>,
577        align_1byte: Vec<u8>,
578        existence_bitmap: Vec<u8>,
579    ) -> Self {
580        Self {
581            id,
582            unique,
583            align_4byte,
584            align_2byte,
585            align_1byte,
586            existence_bitmap,
587        }
588    }
589
590    pub fn id(&self) -> TableRowId {
591        self.id
592    }
593
594    pub fn unique(&self) -> u32 {
595        self.unique
596    }
597
598    pub fn columns(
599        &self,
600        context: &TableContextInfo,
601    ) -> io::Result<Vec<Option<TableRowColumnValue>>> {
602        context
603            .columns()
604            .iter()
605            .map(|column| {
606                let existence_bit = column.existence_bitmap_index() as usize;
607                if !check_existence_bitmap(existence_bit, &self.existence_bitmap)? {
608                    return Ok(None);
609                }
610
611                match (column.prop_type(), column.offset(), column.size()) {
612                    (PropertyType::Null, _, 0) => Ok(None),
613                    (PropertyType::Integer16, offset, 2) => {
614                        let mut cursor = self.read_2byte_offset(context, offset)?;
615                        let value = cursor.read_i16::<LittleEndian>()?;
616                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Integer16(
617                            value,
618                        ))))
619                    }
620                    (PropertyType::Integer32, 0, 4) => Ok(Some(TableRowColumnValue::Small(
621                        PropertyValue::Integer32(u32::from(self.id) as i32),
622                    ))),
623                    (PropertyType::Integer32, 4, 4) => Ok(Some(TableRowColumnValue::Small(
624                        PropertyValue::Integer32(self.unique as i32),
625                    ))),
626                    (PropertyType::Integer32, offset, 4) => {
627                        let mut cursor = self.read_4byte_offset(offset)?;
628                        let value = cursor.read_i32::<LittleEndian>()?;
629                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Integer32(
630                            value,
631                        ))))
632                    }
633                    (PropertyType::Floating32, offset, 4) => {
634                        let mut cursor = self.read_4byte_offset(offset)?;
635                        let value = cursor.read_f32::<LittleEndian>()?;
636                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Floating32(
637                            value,
638                        ))))
639                    }
640                    (PropertyType::Floating64, offset, 8) => {
641                        let mut cursor = self.read_8byte_offset(offset)?;
642                        let value = cursor.read_f64::<LittleEndian>()?;
643                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Floating64(
644                            value,
645                        ))))
646                    }
647                    (PropertyType::Currency, offset, 8) => {
648                        let mut cursor = self.read_8byte_offset(offset)?;
649                        let value = cursor.read_i64::<LittleEndian>()?;
650                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Currency(
651                            value,
652                        ))))
653                    }
654                    (PropertyType::FloatingTime, offset, 8) => {
655                        let mut cursor = self.read_8byte_offset(offset)?;
656                        let value = cursor.read_f64::<LittleEndian>()?;
657                        Ok(Some(TableRowColumnValue::Small(
658                            PropertyValue::FloatingTime(value),
659                        )))
660                    }
661                    (PropertyType::ErrorCode, offset, 4) => {
662                        let mut cursor = self.read_4byte_offset(offset)?;
663                        let value = cursor.read_i32::<LittleEndian>()?;
664                        Ok(Some(TableRowColumnValue::Small(PropertyValue::ErrorCode(
665                            value,
666                        ))))
667                    }
668                    (PropertyType::Boolean, offset, 1) => {
669                        let value = self.read_1byte_offset(context, offset)?;
670                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Boolean(
671                            match value {
672                                0x00 => false,
673                                0x01 => true,
674                                _ => {
675                                    return Err(
676                                        LtpError::InvalidTableColumnBooleanValue(value).into()
677                                    )
678                                }
679                            },
680                        ))))
681                    }
682                    (PropertyType::Integer64, offset, 8) => {
683                        let mut cursor = self.read_8byte_offset(offset)?;
684                        let value = cursor.read_i64::<LittleEndian>()?;
685                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Integer64(
686                            value,
687                        ))))
688                    }
689                    (PropertyType::Time, offset, 8) => {
690                        let mut cursor = self.read_8byte_offset(offset)?;
691                        let value = cursor.read_i64::<LittleEndian>()?;
692                        Ok(Some(TableRowColumnValue::Small(PropertyValue::Time(value))))
693                    }
694                    (
695                        PropertyType::String8
696                        | PropertyType::Unicode
697                        | PropertyType::Guid
698                        | PropertyType::Binary
699                        | PropertyType::Object
700                        | PropertyType::MultipleInteger16
701                        | PropertyType::MultipleInteger32
702                        | PropertyType::MultipleFloating32
703                        | PropertyType::MultipleFloating64
704                        | PropertyType::MultipleCurrency
705                        | PropertyType::MultipleFloatingTime
706                        | PropertyType::MultipleInteger64
707                        | PropertyType::MultipleString8
708                        | PropertyType::MultipleUnicode
709                        | PropertyType::MultipleTime
710                        | PropertyType::MultipleGuid
711                        | PropertyType::MultipleBinary,
712                        offset,
713                        4,
714                    ) => {
715                        let mut cursor = self.read_4byte_offset(offset)?;
716                        let node_id = NodeId::from(cursor.read_u32::<LittleEndian>()?);
717                        let value = match node_id.id_type() {
718                            Ok(NodeIdType::HeapNode) => {
719                                TableRowColumnValue::Heap(HeapId::from(u32::from(node_id)))
720                            }
721                            _ => TableRowColumnValue::Node(node_id),
722                        };
723                        Ok(Some(value))
724                    }
725                    (_, _, size) => Err(LtpError::InvalidTableColumnSize(size).into()),
726                }
727            })
728            .collect()
729    }
730
731    fn read_1byte_offset(&self, context: &TableContextInfo, offset: u16) -> LtpResult<u8> {
732        if offset < context.end_2byte_values() {
733            return Err(LtpError::InvalidTableColumnOffset(offset));
734        }
735        let offset_1byte = (offset - context.end_2byte_values()) as usize;
736        if offset_1byte >= self.align_1byte.len() {
737            return Err(LtpError::InvalidTableColumnOffset(offset));
738        }
739        Ok(self.align_1byte[offset_1byte])
740    }
741
742    fn read_2byte_offset(&self, context: &TableContextInfo, offset: u16) -> LtpResult<&[u8]> {
743        if offset < context.end_4byte_values() {
744            return Err(LtpError::InvalidTableColumnOffset(offset));
745        }
746        let offset_2byte = (offset - context.end_4byte_values()) as usize;
747        if offset_2byte + 2 > self.align_2byte.len() {
748            return Err(LtpError::InvalidTableColumnOffset(offset));
749        }
750        Ok(&self.align_2byte[offset_2byte..offset_2byte + 2])
751    }
752
753    fn read_4byte_offset(&self, offset: u16) -> LtpResult<&[u8]> {
754        if offset < 8 {
755            return Err(LtpError::InvalidTableColumnOffset(offset));
756        }
757        let offset_4byte = (offset - 8) as usize;
758        if offset_4byte + 4 > self.align_4byte.len() {
759            return Err(LtpError::InvalidTableColumnOffset(offset));
760        }
761        Ok(&self.align_4byte[offset_4byte..offset_4byte + 4])
762    }
763
764    fn read_8byte_offset(&self, offset: u16) -> LtpResult<&[u8]> {
765        if offset < 8 {
766            return Err(LtpError::InvalidTableColumnOffset(offset));
767        }
768        let offset_4byte = (offset - 8) as usize;
769        if offset_4byte + 8 > self.align_4byte.len() {
770            return Err(LtpError::InvalidTableColumnOffset(offset));
771        }
772        Ok(&self.align_4byte[offset_4byte..offset_4byte + 8])
773    }
774}
775
776impl TableRowReadWrite for TableRowData {
777    fn read(f: &mut dyn Read, context: &TableContextInfo) -> io::Result<Self> {
778        // dwRowID
779        let id = TableRowId {
780            id: f.read_u32::<LittleEndian>()?,
781        };
782
783        // rgdwData
784        let unique = f.read_u32::<LittleEndian>()?;
785        let mut align_4byte = vec![0; context.end_4byte_values() as usize - 8];
786        f.read_exact(align_4byte.as_mut_slice())?;
787
788        // rgwData
789        let mut align_2byte =
790            vec![0; (context.end_2byte_values() - context.end_4byte_values()) as usize];
791        f.read_exact(align_2byte.as_mut_slice())?;
792
793        // rgbData
794        let mut align_1byte =
795            vec![0; (context.end_1byte_values() - context.end_2byte_values()) as usize];
796        f.read_exact(align_1byte.as_mut_slice())?;
797
798        // rgbCEB
799        let mut existence_bitmap = vec![0; existence_bitmap_size(context.columns().len())];
800        f.read_exact(existence_bitmap.as_mut_slice())?;
801
802        Ok(Self::new(
803            id,
804            unique,
805            align_4byte,
806            align_2byte,
807            align_1byte,
808            existence_bitmap,
809        ))
810    }
811
812    fn write(&self, f: &mut dyn Write) -> io::Result<()> {
813        f.write_u32::<LittleEndian>(u32::from(self.id))?;
814        f.write_u32::<LittleEndian>(self.unique)?;
815        f.write_all(&self.align_4byte)?;
816        f.write_all(&self.align_2byte)?;
817        f.write_all(&self.align_1byte)?;
818        f.write_all(&self.existence_bitmap)
819    }
820}
821
822pub trait TableContext {
823    fn context(&self) -> &TableContextInfo;
824    fn rows_matrix<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = &'a TableRowData>>;
825    fn find_row(&self, id: TableRowId) -> LtpResult<&TableRowData>;
826    fn read_column(
827        &self,
828        value: &TableRowColumnValue,
829        prop_type: PropertyType,
830    ) -> io::Result<PropertyValue>;
831}
832
833struct TableContextInner<Pst, RowIndex, RowIndexTree>
834where
835    Pst: PstFile,
836    RowIndex: TableRowIndex<Pst>,
837    RowIndexTree: TableRowIndexTree<Pst, RowIndex = RowIndex>,
838    u32: From<RowIndex>,
839{
840    store: Rc<<Pst as PstFile>::Store>,
841    node: <Pst as PstFile>::NodeBTreeEntry,
842    context: TableContextInfo,
843    heap: <Pst as PstFile>::HeapNode,
844    row_index: BTreeMap<TableRowId, RowIndex>,
845    rows: Vec<TableRowData>,
846    block_cache: RefCell<DataBlockCache<Pst>>,
847    _phantom: PhantomData<RowIndexTree>,
848}
849
850impl<Pst, RowIndex, RowIndexTree> TableContextInner<Pst, RowIndex, RowIndexTree>
851where
852    Pst: PstFile + PstFileLock<Pst>,
853    <Pst as PstFile>::BlockId: BlockId<Index = <Pst as PstFile>::BTreeKey> + BlockIdReadWrite,
854    <Pst as PstFile>::ByteIndex: ByteIndexReadWrite,
855    <Pst as PstFile>::BTreeKey: BTreePageKeyReadWrite,
856    <Pst as PstFile>::BlockBTree: RootBTreeReadWrite,
857    <<Pst as PstFile>::BlockBTree as RootBTree>::Entry: BTreeEntryReadWrite,
858    <<Pst as PstFile>::BlockBTree as RootBTree>::IntermediatePage:
859        RootBTreeIntermediatePageReadWrite<
860            Pst,
861            <<Pst as PstFile>::BlockBTree as RootBTree>::Entry,
862            <<Pst as PstFile>::BlockBTree as RootBTree>::LeafPage,
863        >,
864    <<Pst as PstFile>::BlockBTree as RootBTree>::LeafPage:
865        RootBTreeLeafPageReadWrite<Pst> + BTreePageReadWrite,
866    <Pst as PstFile>::BlockTrailer: BlockTrailerReadWrite,
867    <Pst as PstFile>::DataTreeBlock: IntermediateTreeBlockReadWrite,
868    <<Pst as PstFile>::DataTreeBlock as IntermediateTreeBlock>::Entry:
869        IntermediateTreeEntryReadWrite,
870    <Pst as PstFile>::DataBlock: BlockReadWrite + Clone,
871    <Pst as PstFile>::SubNodeTreeBlockHeader: IntermediateTreeHeaderReadWrite,
872    <Pst as PstFile>::SubNodeTreeBlock: IntermediateTreeBlockReadWrite,
873    <<Pst as PstFile>::SubNodeTreeBlock as IntermediateTreeBlock>::Entry:
874        IntermediateTreeEntryReadWrite,
875    <Pst as PstFile>::SubNodeBlock: IntermediateTreeBlockReadWrite,
876    <<Pst as PstFile>::SubNodeBlock as IntermediateTreeBlock>::Entry:
877        IntermediateTreeEntryReadWrite,
878    <Pst as PstFile>::HeapNode: HeapNodeReadWrite<Pst> + From<RowIndexTree>,
879    <Pst as PstFile>::Store: StoreReadWrite<Pst>,
880    RowIndex: TableRowIndex<Pst>,
881    RowIndexTree: TableRowIndexTree<Pst, RowIndex = RowIndex>,
882    u32: From<RowIndex>,
883{
884    fn read(
885        store: Rc<<Pst as PstFile>::Store>,
886        node: <Pst as PstFile>::NodeBTreeEntry,
887    ) -> io::Result<Self> {
888        let mut file = store
889            .pst()
890            .reader()
891            .lock()
892            .map_err(|_| LtpError::FailedToLockFile)?;
893        let file = &mut *file;
894
895        let header = store.pst().header();
896        let encoding = header.crypt_method();
897        let mut page_cache = store.pst().block_cache();
898        let block_btree = <<Pst as PstFile>::BlockBTree as RootBTreeReadWrite>::read(
899            file,
900            *header.root().block_btree(),
901        )?;
902
903        let data = node.data();
904        let heap = <<Pst as PstFile>::HeapNode as HeapNodeReadWrite<Pst>>::read(
905            file,
906            &block_btree,
907            &mut page_cache,
908            encoding,
909            data.search_key(),
910        )?;
911        let header = HeapNode::header(&heap)?;
912        let mut block_cache: DataBlockCache<Pst> = Default::default();
913
914        let mut cursor = Cursor::new(heap.find_entry(header.user_root())?);
915        let context = TableContextInfo::read(&mut cursor)?;
916
917        let rows = if let Some(rows) = context.rows {
918            match rows.id_type() {
919                Ok(NodeIdType::HeapNode) => {
920                    let rows: u32 = rows.into();
921                    vec![heap.find_entry(HeapId::from(rows))?.to_vec()]
922                }
923                _ => {
924                    let sub_node = node
925                        .sub_node()
926                        .ok_or(LtpError::PropertySubNodeValueNotFound(rows.into()))?;
927                    let block =
928                        block_btree.find_entry(file, sub_node.search_key(), &mut page_cache)?;
929                    let sub_node_tree = SubNodeTree::<Pst>::read(file, &block)?;
930                    let block =
931                        sub_node_tree.find_entry(file, &block_btree, rows, &mut page_cache)?;
932                    let block =
933                        block_btree.find_entry(file, block.search_key(), &mut page_cache)?;
934                    let data_tree = match block_cache.remove(&block.block().block()) {
935                        Some(data_tree) => data_tree,
936                        None => DataTree::read(file, encoding, &block)?,
937                    };
938                    let result = data_tree
939                        .blocks(
940                            file,
941                            encoding,
942                            &block_btree,
943                            &mut page_cache,
944                            &mut block_cache,
945                        )
946                        .map(|blocks| {
947                            blocks
948                                .map(|block| block.data().to_vec())
949                                .collect::<Vec<_>>()
950                        });
951                    block_cache.insert(block.block().block(), data_tree);
952                    result?
953                }
954            }
955            .into_iter()
956            .map(|data| {
957                let row_count = data.len() / context.end_existence_bitmap() as usize;
958                let mut cursor = Cursor::new(data);
959                let mut rows = Vec::with_capacity(row_count);
960                for _ in 0..row_count {
961                    let row = TableRowData::read(&mut cursor, &context)?;
962                    rows.push(row);
963                }
964                Ok(rows)
965            })
966            .collect::<io::Result<Vec<_>>>()?
967            .into_iter()
968            .flatten()
969            .collect()
970        } else {
971            Default::default()
972        };
973
974        let row_index_tree = RowIndexTree::new(heap, context.row_index);
975        let row_index = row_index_tree
976            .entries()?
977            .into_iter()
978            .map(|entry| (entry.key(), entry.data()))
979            .collect();
980        let heap = row_index_tree.into();
981
982        Ok(Self {
983            store: store.clone(),
984            node,
985            context,
986            heap,
987            row_index,
988            rows,
989            block_cache: RefCell::new(block_cache),
990            _phantom: PhantomData,
991        })
992    }
993
994    fn read_column(
995        &self,
996        value: &TableRowColumnValue,
997        prop_type: PropertyType,
998    ) -> io::Result<PropertyValue> {
999        match value {
1000            TableRowColumnValue::Small(small) => Ok(small.clone()),
1001            TableRowColumnValue::Heap(heap_id) => {
1002                let data = self.heap.find_entry(*heap_id)?;
1003                let mut cursor = Cursor::new(data);
1004                PropertyValueReadWrite::read(&mut cursor, prop_type)
1005            }
1006            TableRowColumnValue::Node(sub_node_id) => {
1007                let mut file = self
1008                    .store
1009                    .pst()
1010                    .reader()
1011                    .lock()
1012                    .map_err(|_| LtpError::FailedToLockFile)?;
1013                let file = &mut *file;
1014
1015                let encoding = self.store.pst().header().crypt_method();
1016                let block_btree = self.store.block_btree();
1017                let mut page_cache = self.store.pst().block_cache();
1018
1019                let sub_node =
1020                    self.node
1021                        .sub_node()
1022                        .ok_or(LtpError::PropertySubNodeValueNotFound(
1023                            (*sub_node_id).into(),
1024                        ))?;
1025                let block = block_btree.find_entry(file, sub_node.search_key(), &mut page_cache)?;
1026                let sub_node_tree = SubNodeTree::<Pst>::read(file, &block)?;
1027                let block =
1028                    sub_node_tree.find_entry(file, block_btree, *sub_node_id, &mut page_cache)?;
1029                let block = block_btree.find_entry(file, block.search_key(), &mut page_cache)?;
1030                let mut block_cache = self.block_cache.borrow_mut();
1031                let data_tree = match block_cache.remove(&block.block().block()) {
1032                    Some(data_tree) => data_tree,
1033                    None => DataTree::read(file, encoding, &block)?,
1034                };
1035                let result = data_tree
1036                    .reader(
1037                        file,
1038                        encoding,
1039                        block_btree,
1040                        &mut page_cache,
1041                        &mut block_cache,
1042                    )
1043                    .and_then(|mut r| PropertyValueReadWrite::read(&mut r, prop_type));
1044                block_cache.insert(block.block().block(), data_tree);
1045                result
1046            }
1047        }
1048    }
1049}
1050
1051type UnicodeRowIndexTree = UnicodeHeapTree<TableRowId, UnicodeTableRowIndex>;
1052
1053impl TableRowIndexTree<UnicodePstFile> for UnicodeRowIndexTree {
1054    type RowIndex = UnicodeTableRowIndex;
1055}
1056
1057pub struct UnicodeTableContext {
1058    inner: TableContextInner<UnicodePstFile, UnicodeTableRowIndex, UnicodeRowIndexTree>,
1059}
1060
1061impl UnicodeTableContext {
1062    pub fn read(
1063        store: Rc<UnicodeStore>,
1064        node: UnicodeNodeBTreeEntry,
1065    ) -> io::Result<Rc<dyn TableContext>> {
1066        <Self as TableContextReadWrite<UnicodePstFile>>::read(store, node)
1067    }
1068}
1069
1070impl TableContext for UnicodeTableContext {
1071    fn context(&self) -> &TableContextInfo {
1072        &self.inner.context
1073    }
1074
1075    fn rows_matrix<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = &'a TableRowData>> {
1076        Box::new(self.inner.rows.iter())
1077    }
1078
1079    fn find_row(&self, id: TableRowId) -> LtpResult<&TableRowData> {
1080        let index = self
1081            .inner
1082            .row_index
1083            .get(&id)
1084            .ok_or(LtpError::TableRowIdNotFound(u32::from(id)))?;
1085        Ok(&self.inner.rows[u32::from(*index) as usize])
1086    }
1087
1088    fn read_column(
1089        &self,
1090        value: &TableRowColumnValue,
1091        prop_type: PropertyType,
1092    ) -> io::Result<PropertyValue> {
1093        self.inner.read_column(value, prop_type)
1094    }
1095}
1096
1097impl TableContextReadWrite<UnicodePstFile> for UnicodeTableContext {
1098    fn read(
1099        store: Rc<UnicodeStore>,
1100        node: UnicodeNodeBTreeEntry,
1101    ) -> io::Result<Rc<dyn TableContext>> {
1102        let inner = TableContextInner::read(store, node)?;
1103        Ok(Rc::new(Self { inner }))
1104    }
1105}
1106
1107type AnsiRowIndexTree = AnsiHeapTree<TableRowId, AnsiTableRowIndex>;
1108
1109impl TableRowIndexTree<AnsiPstFile> for AnsiRowIndexTree {
1110    type RowIndex = AnsiTableRowIndex;
1111}
1112
1113pub struct AnsiTableContext {
1114    inner: TableContextInner<AnsiPstFile, AnsiTableRowIndex, AnsiRowIndexTree>,
1115}
1116
1117impl AnsiTableContext {
1118    pub fn read(
1119        store: Rc<AnsiStore>,
1120        node: AnsiNodeBTreeEntry,
1121    ) -> io::Result<Rc<dyn TableContext>> {
1122        <Self as TableContextReadWrite<AnsiPstFile>>::read(store, node)
1123    }
1124}
1125
1126impl TableContext for AnsiTableContext {
1127    fn context(&self) -> &TableContextInfo {
1128        &self.inner.context
1129    }
1130
1131    fn rows_matrix<'a>(&'a self) -> Box<dyn 'a + Iterator<Item = &'a TableRowData>> {
1132        Box::new(self.inner.rows.iter())
1133    }
1134
1135    fn find_row(&self, id: TableRowId) -> LtpResult<&TableRowData> {
1136        let index = self
1137            .inner
1138            .row_index
1139            .get(&id)
1140            .ok_or(LtpError::TableRowIdNotFound(u32::from(id)))?;
1141        Ok(&self.inner.rows[u32::from(*index) as usize])
1142    }
1143
1144    fn read_column(
1145        &self,
1146        value: &TableRowColumnValue,
1147        prop_type: PropertyType,
1148    ) -> io::Result<PropertyValue> {
1149        self.inner.read_column(value, prop_type)
1150    }
1151}
1152
1153impl TableContextReadWrite<AnsiPstFile> for AnsiTableContext {
1154    fn read(store: Rc<AnsiStore>, node: AnsiNodeBTreeEntry) -> io::Result<Rc<dyn TableContext>> {
1155        let inner = TableContextInner::read(store, node)?;
1156        Ok(Rc::new(Self { inner }))
1157    }
1158}