1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Содержит описания структур заголовка GFF файла

use std::cmp::max;
use std::io::{Read, Write, Result};
use byteorder::{LE, ReadBytesExt, WriteBytesExt};

pub use sig::*;
pub use ver::*;

/// Описание области файла, описывающей местоположение списков записей в файле
#[derive(Debug, Default)]
pub struct Section {
  /// Смещение в байтах от начала файла в сериализованном виде
  pub offset: u32,
  /// Количество записей по смещению `offset`. Размер записи зависит от конкретного поля
  pub count:  u32,
}

impl Section {
  /// Читает описание области из потока
  #[inline]
  pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
    Ok(Section {
      offset: reader.read_u32::<LE>()?,
      count:  reader.read_u32::<LE>()?,
    })
  }
  /// Записывает описание области файла в поток
  #[inline]
  pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
    writer.write_u32::<LE>(self.offset)?;
    writer.write_u32::<LE>(self.count)
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/// Заголовок GFF файла. Заголовок содержит вид файла, версию формата и информацию о
/// 6 областях, файла, содержащих данные:
/// - Список структур в файле
/// - Общий список полей всех структур файла
/// - Список уникальных названий полей
/// - Список с данными полей
/// - Вспомогательный список для индексов для сложных структур данных
/// - Вспомогательный список для хранения списочных значений полей
#[derive(Debug)]
pub struct Header {
  /// Конкретный вид GFF файла
  pub signature: Signature,
  /// Версия файла
  pub version: Version,

  /// Содержит смещение в байтах от начала файла области с расположением
  /// структур и их количество
  pub structs: Section,

  /// Содержит смещение в байтах от начала файла области с расположением
  /// полей структур и их количество
  pub fields: Section,

  /// Содержит смещение в байтах от начала файла области с расположением
  /// меток полей в структурах и их количество
  pub labels: Section,

  /// Содержит смещение в байтах от начала файла области с расположением
  /// сериализованных значений полей и суммарное число байт данных
  pub field_data: Section,

  /// Содержит смещение в байтах от начала файла области с расположением
  /// индексов полей и их количество
  pub field_indices: Section,

  /// Содержит смещение в байтах от начала файла области с расположением
  /// индексов списков и их количество
  pub list_indices: Section,
}

impl Header {
  /// Создает заголовок для пустого файла с указанным типом
  #[inline]
  pub fn new(signature: Signature) -> Self {
    Self::with_version(signature, Version::V3_2)
  }
  /// Создает заголовок для пустого файла с указанным типом и версией
  #[inline]
  pub fn with_version(signature: Signature, version: Version) -> Self {
    Header {
      signature,
      version,
      structs:       Section::default(),
      fields:        Section::default(),
      labels:        Section::default(),
      field_data:    Section::default(),
      field_indices: Section::default(),
      list_indices:  Section::default(),
    }
  }
  /// Читает значение GFF заголовка из потока
  pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
    Ok(Header {
      signature:     Signature::read(reader)?,
      version:       Version::read(reader)?,

      structs:       Section::read(reader)?,
      fields:        Section::read(reader)?,
      labels:        Section::read(reader)?,
      field_data:    Section::read(reader)?,
      field_indices: Section::read(reader)?,
      list_indices:  Section::read(reader)?,
    })
  }
  /// Записывает значение GFF заголовка в поток
  pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
    self.signature.write(writer)?;
    self.version.write(writer)?;

    self.structs.write(writer)?;
    self.fields.write(writer)?;
    self.labels.write(writer)?;
    self.field_data.write(writer)?;
    self.field_indices.write(writer)?;
    self.list_indices.write(writer)
  }
  /// Возвращает нижнюю границу на количество токенов, которые может произвести
  /// данный файл
  #[inline]
  pub fn token_count(&self) -> usize {
    // Для каждой структуры - токен начала и окончания
    // Для каждого списка - токен начала и окончания
    let size = (self.structs.count + self.list_indices.count)*2;

    // Т.к. каждое поле может быть списком или структурой, то они уже подсчитываются
    // в списках и структурах. Поэтому минимальное количество вычисляем, как максимум
    // из того, что нам смогут дать поля или структуры со списками
    max(size, self.fields.count) as usize
  }
}