О С Н О В Н О Й T P S - Ф А Й Л
ФИЗИЧЕСКАЯ ОРГАНИЗАЦИЯ
Заголовок
(+0) (+4) (+6) (+0Ah) (+0Eh)
00 00 00 00 00 00 00 05 00 00 00 05 00 00 74 4F 70 53
UNSIGNED LONG USHORT UNSIGNED LONG UNSIGNED LONG UNSIGNED LONG
Смещение Размер Длина файла Длина файла Метка файла
заголовка заголовка TOPSPEED
(+12h) (+14h) (+18h) (+1Ch)
00 00 00 00 00 06 07 00 00 00 00 00 00 00
SHORT UNSIGNED LONG UNSIGNED LONG UNSIGNED LONG
Нули Последний Счетчик изменений Верхняя управляющая
(?) номер файла страница (адрес минус 200h,
деленный на 100h)
Далее следуют 2 массива длиной (длина_заголовка-20h)/2 четырехбайтовых
чисел (ниже порядок байт перевернут):
0020h: 00000000 00000000 01000000 04000000 4D000000 22010000 22010000
0110h: 00000000 01000000 03000000 4C000000 22010000 22010000 22010000
^ ^
эта пара элементов это (длина_файла-200h)/100h,
игнорируется (?) эта пара чисел заполняет остаток массивов
В файле страницы идут блоками, между которыми простанство не используется.
i-й элемент в первом массиве показывает начало каждого такого блока, i-й
элемент во втором - конец блока. i-й элемент - это смещение блока (первого
байта в первой странице для первого массива и первого байта следующей за по-
следней страницей страницы в блоке для второго массива) минус 200h (размер
заголовка), деленное на 100h. Все страницы в блоке, кроме, быть может,
последней, сжаты, если могут быть сжаты.
Если какая-то страница в середине блока не сжата, но может быть сжата, то
блок разбивается на два так, чтобы несжатая страница была в конце блока. В этих
массивах тогда встречается конструкция вида (в примере ниже несжатая страница
находится по смещению 0200h и имеет размер 100h):
0020h: 00000000 00000000 01000000
0110h: 00000000 01000000 22010000
^ ^
В конце файла не может быть неиспользуемого пространства. Если оно по-
является, то файл укорачивается (обрезается функцией int 21h, ah=40h, cx=0).
Неизвестно, может ли заголовок быть длиннее 200h, и если да, то что
происходит с этими массивами (скорее всего, их длина просто увеличивается).
Формат обычной страницы
(+0) (+4) (+6) (+8)
00 02 00 00 73 00 77 00 7F 00
ULONG USHORT USHORT USHORT
Смещение Длина сжатой Длина страницы Длина страницы
страницы страницы (если страница после деком- после декомпрес-
(для контроля) не сжата, то здесь по- прессии сии безо всех
вторяется следуещее поле) сокращений
(+0Ah) (+0Ch) (+0Dh)
0A 00 00 05
USHORT BYTE BYTE
Число записей Признак обычной Смещение первого блока-повторителя
на странице страницы Этот байт есть только когда
(уровень страницы) страница сжата,
т.е. поле по смещению 4 не
равно полю по смещению 6
(+0Dh/0Eh)
...
Далее идут записи на странице
Страницы в файле имеют разную длину. Длина конкретной страницы зависит от
файлового драйвера. Если при добавлении/изменении данных оказывается, что
страницы слишком длинная, она разделяется на две.
За страницей неиспользуемое пространство до следующего кратного 0x100
смещения заполяется байтом 0xB0 и не входит в длину страницы (поля +4, +6, +8),
но резеврируется за ней и в заголовке указывается принадлежащим странице.
Формат управляющей страницы
(+0) (+4) (+6) (+8)
00 02 00 00 73 00 77 00 7F 00
ULONG USHORT USHORT USHORT
Смещение Длина страницы Длина страницы Длина страницы
страницы с сокращениями с сокращениями без
(для контроля) сокращений
(+0Ah) (+0Ch)
0A 00 00
USHORT BYTE Байта по смещению +0Dh
Число записей Уровень управляющей нет, т.к. управляющие страницы
на странице страницы не сжимаются (?)
(0-обычная страница)
(+0Dh)
00 00 00 00 05 00 00 00 ...
ULONG ULONG
Массив подчиненных страниц. Его размер равен числу записей на упр.странице.
Элемент массива - это (смещение_подчиненной_страницы-200h)/100h.
(+?)
...
Далее следуют записи. Они повторяют первые записи на подчиненных страницах
(возможно укороченные, если подчиненные страницы не управляющие).
Каждой этой записи соответствует элемент в массиве подчиненных страниц.
Сжатые страницы
Если страница сжатая, то поля (+4) и (+6) заголовка страницы различаются.
В этом случае байт по смещению (+0Dh) показывает смещение относительно
байта (+Eh) первого блока-повторителя. Формат этого блока:
00 05 03
BYTE BYTE BYTE
Какой байт Кол-во повторений Смещение следующего
нужно повторять минус одно блока-повторителя
Смещение следующего блока-повторителя - это смещение следующего такого блока
относительно байта, последнего в этом блоке. Если блок - последний, то смещение
следующего блока указывает на последний байт страницы (не на следующий за
страницей байт 0xB0).
Если количество повторений > 127, то непосредственно за этим байтом следует
еще один:
3E 85 10 03
BYTE BYTE BYTE BYTE
Какой байт Первый байт Второй байт Смещение следующего
повторять Кол-во повторений минус одно блока
Кол-во повторений тогда вычисляется по формуле
((второй_байт + ((первый_байт & 7F) << 1)) >> 1) + 1
Если смещение_следующего_блока > 127, то непосредственно за этим байтом
следует еще один байт, смещение относительно последнего байта блока при этом
вычисляется по формуле
(второй_байт + ((первый_байт & 7F) << 1)) >> 1
Заголовок страницы не сжимается. Нормальное состояние страниц - сжатое.
Однако страница не сжимается, если не может быть сжата, т.е.после сжатия ее
длина должна быть строго меньше несжатой. Если страница не сжата, но может
быть сжата, то она указывается в заголовке. Если страница не может быть сжата,
то она в заголовке не указывается.
Общий формат записи на странице
(+0) (+1) (+1/3) (+1/3/5)
С0 2A 00 09 00 . . .
Идентификатор Новая длина Новая длина
записи записи в Данные
управляющей
странице
Формат идентификатора: 1 1 0 0 0 1 0 0
│ │ L----T-----
указана новая длина записи --- │ │
│ сколько первых байт этой
указана новая длина записи ----- записи должно быть взято
в управляющей странице из предыдущей записи
Длина новой записи присутствует только тогда, когда идентификатор & 0x80<>0,
в противном случае эначение длины берется из предыдущей записи. Длина новой
записи в управляющей странице присутствует только тогда, когда
идентификатор & 0x40 <> 0, в противном случае берется из предыдущей записи.
Для первой записи на странице эти оба значения должны быть обязательно указаны,
т.е. должно выполняться идентификатор & 0xC0 = 0xC0. Шесть младших битов иден-
тификатора показывают количество копируемых из предыдущей записи байтов.
Общий формат файла
---- Заголовок ------┐ ---->--- Блок 1
│ ... │ │ │-- страница 1
│ верхняя страница │ │ ││ .....--
│ ... │ │ │L-
│ │ │ │-- страница 2
│ 020h: 00 00 00 00 │ │ ││ ...........------
│ начало1 -------- │L-
│ начало2 ---------┐ │...
│ начало3 -------┐ │ │-- страница M
│ ... │ │ │ ││ ..........---
│ 110h: 00 00 00 00 │ │ │ │L-
│ конец1 --------+-+->L----
│ конец2 --------+ │ неиспользуемое пространство
│ конец3 ------┐ │ L->--- Блок 2
│ ... ││ │ │-- страница 1
L---------------------│ │ ││ ...
│ │ │L-
│ │ │-- страница 2
│ │ ││ ...
│ │ │L-
│ │ │...
│ │ │-- страница N - незаархивированная
│ │ ││ ...
│ │ │L-
│ │ L----
│ L--->--- Блок 3
│ │-- страница 1
│ ││ ...
│ │L-
│ │...
│ │-- страница K
│ ││ ...
│ │L-
│ L----
L-----> неиспользуемое пространство
--- Блок L
...
L----
Конец файла
-- Заголовок ----------------┐
│ . . . . . . . . . . . . │
---< страница верхнего уровня │
│ │ . . . . . . . . . . . . │
│ L-----------------------------
│
│ Страница
│ уровня N Страницы
L->T-----------------┐ уровня N-1 Страницы
│ подчинен.стр.11-------->T-------------------┐ нулевого уровня
│ подчинен.стр.12------┐ │ подч.стр.21--------->...->--------------------┐
│ ... │ │ │ подч.стр.22 │ --->записьN1=запись11 │
│ │ │ │ ... │ │ │ записьN2 │
│-----------------│ │ │ │ │ │ ... │
│ запись11 ------------+-┐│-------------------│ │ L--------------------
│ запись12 -----------┐│ L->запись21=запись11 ------- ...
│ ... │ ││ │ запись22 │
L------------------ ││ │ ... │ ...->--------------------┐
││ L-------------------- ->записьM1 │
││ │ записьM2 │
│L->--------------------┐ │ │
│ │ подч.стр.31--------->... L--------------------
│ │ ... │ ...
│ │-------------------│ ...->--------------------┐
L---->запись31=запись12---------->записьK1 │
│ ... │ │ записьK2 │
L-------------------- │ ... │
... L--------------------
...
Все записи отсортированы в лексикографическом порядке.
Все записи в файле отсортированы в лексикографическом порядке. Это
достигается тем, что на каждой странице записи отсортированы, в том числе и на
управляющих. На управляющих страницах записи повторяют первые записи на
соответствующих подчиненных страницах (или несколько укороченные). Физическая
организация TPS-файла сильно похожа на организацию ключевых/индексных файлов
формата CLARION 2.1.
Замечание: судя по всему, в файле (и на каждой странице) не должно быть двух
одинаковых записей (см.записи ключей/индексов и атрибут DUP).
При поиске записи проходится цепочка страниц от верхней управляющей до
страницы нулевого уровня, по одной странице из каждого уровня. На странице
поиск начинается от начала страницы: ищется последняя запись, которая лексико-
графически меньше искомой. Дальше для найденной записи и для каждой записи,
N первых символов которой совпадают с искомой (N - длина искомой записи),
запись ищется на подчиненной странице (или считается найденной, если это
страница нулевого уровня). Если первая запись на странице лексикографически
больше искомой, то на этой странице и на подчиненных искомой записи нет. Если
на странице нулевого уровня первая запись меньше искомой, а после последней
записи, N первых символов которой меньше искомой, сразу идет запись, которая
лексикографически больше искомой, то на этой странице и во всем файле искомой
записи нет.
При добавлении новой записи в файл проходится цепочка страниц длиной M, где
M - уровень верхней управляющей страницы, и определяется место, куда поместить
новую запись, чтобы она не нарушала порядка сортировки.
ЛОГИЧЕСКАЯ ОРГАНИЗАЦИЯ БАЗ ДАННЫХ
TPS-файл может содержать несколько таблиц данных и для каждой таблицы
данные, ключи/индексы и memo-поля.
Типы и форматы записей
00-F2 - ключи/индексы
F3 - запись базы данных
F6 - информационная запись, содержит число записей конкретного типа
FA - описание таблицы
FC - данные memo-полей
Пустая запись
Первой записью в файле всегда идет пустая запись: C0 00 00 00 00
Запись базы данных
(+0) (+4)
C0 3F 00 09 00 00 00 00 01 F3
BYTE USHORT USHORT ULONG BYTE
Идентификатор Длина Длина записи Номер таблицы Код записи -
записи+9 в упр.странице, запись БД
всегда равна 9
(+5) (+9)
00 00 00 1B ...
ULONG
Номер записи, Данные
уникальное число
для каждой записи
При добавлении новой записи номер последней записи в заголовке файла увеличи-
вается на единицу (последний байт увеличивается первым, как в процессорах
Motorola), а затем присваивается новой записи. Таким образом достигается
уникальность. При физическом создании файла DOS номер последней записи
устанавливается в 1 (в формате Motorola).
Длина записи в упр.странице - всегда 9.
Запись ключа/индекса
(+0) (+4)
C0 19 00 19 00 00 00 00 01 01
BYTE USHORT USHORT ULONG BYTE
Идентификатор Номер таблицы Код записи, одновременно
порядковый номер ключа
(+5) (+?)
80 02 80 05 05 ... 00 00 00 1B
ULONG
Данные Номер записи БД
Сортировка в ключе/индексе производится благодаря обязательной сортировке
всех записей в файле. Длина этой записи в управляющей странице равна длина
записи в обычной странице, если ключ имеет атрибут DUP, и меньше ее на 4 в
противном случае (очевидно, в TPS-файле не должно быть двух одинаковых
записей).
Замечание: если номер записи в индексе ссылается на несуществующую запись БД
(если его исправить вручную), то при следующих построениях индекса Clarion
Database Manager'ом она иногда удалена не будет (почему?).
Запись memo-поля
(+0) (+4) (+5)
C0 0C 01 0C 00 00 00 00 01 FC 00 00 00 02
BYTE USHORT USHORT ULONG BYTE ULONG
Номер таблицы Код записи - Номер записи БД,
данные memo к которой относится
это memo-поле
(+9) (+0Ah) (+0Ch)
00 00 01 ...
BYTE USHORT
Порядковый номер Номер блока memo, Данные memo-поля
memo-поля посл.байт изменяется
(у записи может быть первым
больше одного memo-поля)
Данные memo следуют блоками по 256 байт (в последнем блоке может быть меньше)
Номер блока указывается в поле (+0Ah) (последний байт которого изменяется
первым). Длина этой записи в упр.странице - всегда 12.
Информационная запись
(+0) (+4) (+5)
C0 0E 00 06 00 00 00 00 01 F6 01
BYTE USHORT USHORT ULONG BYTE BYTE
Номер таблицы Код записи - Код записей, к которым
информационная относится информация:
запись 00-F2 - ключи/индексы
F3 - данные
(+6) (+0Ah)
05 00 00 00 00 00 00 00
ULONG ULONG
Число записей Запись, к которой
с этим кодом было обращение
По одной записи этого типа создается для каждого ключа/индекса и для данных
таблицы. Код записей, для которых создается эта запись, указывается в поле по
смещению +5. Длина этой записи в упр.странице - всегда 6.
Запись, к которой было обращение, равна 0, если индекс не требует перестро-
ения, и содержит номер записи БД, к которой первый раз обратились после
последнего перестроения индекса. Всегда равно 0 для ключей и данных таблицы.
Описание структуры таблицы
(+0) (+4) (+5) (+7)
C0 1A 00 07 00 00 00 00 01 FA 00 00 01 00
BYTE USHORT USHORT ULONG BYTE USHORT USHORT
Номер таблицы Код записи - Номер Минимальная
описание блока версия драйвера
структуры описания для работы
3F 00 07 00 01 00 07 00
USHORT USHORT USHORT USHORT
Длина Число Число Число
записи в полей memo ключей/
таблице индексов
Данные описания структуры следуют блоками по 512 байт (в последнем блоке
может быть меньше) (аналогично memo). Номер блока указывается в поле (+5).
Длина каждой такой записи в упр.странице - всегда 7. В поле (+7) стоит
минимальная версия драйвера для работы с файлом:
1 = TopSpeed 1.0 из пакета Clarion 3.1
2 = TopSpeed из пакета Clarion for Window 1.5
Описание полей
--------------
(+0) (+1) (+3) (+n) (+n+2)
12 00 00 FIELD1 00 01 00 14 00
BYTE USHORT CSTRING SHORT SHORT
Тип Смещение поля Название Число эл-тов Длина всего
поля от начала записи поля в массиве массива
(+n+4) (+n+6)
00 00 01 00
USHORT USHORT
? Порядковый
равно 1, если это поле номер поля
перекрывает другое в записи
поле (атрибут OVER)
равно 0 в пр.случае
Поле (+n) содержит 1, если это поле не массив, размерность для одномерного
массива и произведение размерностей для многомерного размера. Следующее поле
содержит размер всего массива, т.е.размер одного эл-та, умноженный на содер-
жимое поля (+n+2).
Если файл имеет префикс, то он указывается в каждом имени поля, например:
"TST:FIELD1". Если префикса нет, то пишется просто "FIELD1". Префикс полей
никак не учитывается после создания.
Замечание: если в поле (+n+4) поместить любое не равное 0 значение, то это
будет действовать также, как и единица. Однако стандартные средства помещают 1.
Типы полей
----------
Тип Размер Название Описание
--- ------ -------- --------
01 1 BYTE число без знака
02 2 SHORT число со знаком в формате Intel 8086
03 2 USHORT SHORT без знака
06 4 LONG число со знаком в формате Intel 8086
07 4 ULONG LONG без знака
08 4 SREAL формат single сопроцессора Intel 8087
09 8 REAL формат double сопроцессора Intel 8087
0A ? DECIMAL дополнительные данные:
(+n+8) (+n+9)
02 05
BYTE BYTE
Число знаков Длина одного
после десятич.точки элемента массива
DECIMAL - это число с фиксированной точкой в формате BCD, один байт содержит
две цифры, старший ниббл старшего байта содержит знак (0-плюс, другое-минус).
Замечание: Clarion записывает в качестве минуса 0xF. Старший байт хранится
первым (с наименьшим смещением). Размер данных вычисляется по формуле:
(число_знаков_до_точки + число_знаков_после_точки)/2 + 1
Максимальная длина - 16 байт.
12 ? STRING дополнительные данные:
(+n+8) (+n+10)
14 00 00 00
USHORT ?
Длина одного Шаблон
эл-та массива picture
Если описываемое поле - массив, то (+n+8) - длина одного его элемента.
Если строка имеет шаблон, то он должен быть указан в поле (+n+10), без
знака @ и завершающийся одним нулем. Если шаблона нет, то поле (+n+10)
содержит 2 байта: первый - нуль, второй - любое число (Clarion 3.1
записывает 0). Почему два байта - непонятно.
12 ? PICTURE дополнительные данные:
(+n+8) (+n+10)
09 00 p####-####p 00
USHORT CSTRING
Длина одного picture
эл-та массива
13 ? CSTRING дополнительные данные: см. STRING или PICTURE
CSTRING - строка, заканчивающаяся нулем.
14 ? PSTRING дополнительные данные: см. STRING или PICTURE
PSTRING - строка, первый байт которой - длина
Размер поля PSTRING - макс.длина строки плюс 1.
16 ? GROUP дополнительных данных нет
GROUP представляется как независимое от вложенных в него полей поле.
Оно просто перекрывает другие поля (с помощью указания смещения и размера).
Номер ему присваивается также, как и другим полям, независимо от других
полей. Располагается непосредственно перед подчиненными полями, которые
следуют за ним. Номера подчиненным полям присваюваются, как будто они не
входят в GROUP. Если группа имеет префикс, это указывается в названии
подчиненных полей и замещает префикс файла, например: "GRP:FIRST_FIELD".
Если группа не имеет префикса, на ее поля имеют тот же префикс, что у
основного файла. Если и у основного файла нет префикса, то ее поля не
имеют никакого префикса. Префикс полей группы никак не учитывается после
создания.
При создании массива GROUP размерность его указывается в поле (+n) в
описании поля GROUP. Больше ссылок на то, что это массив, нет. Для
подчиненных элементов смещение указывается как для первого элемента
массива. Подчиненное поле из первого элемента массива может даже входить
в ключ.
Описание memo
-------------
(+0) (+n) (+m) (+m+2)
DATA.MEM 00 FILE_MEMO 00 10 27 01 00
CSTRING CSTRING USHORT USHORT
Название внешнего Название Длина Атрибуты
файла для memo memo-поля memo-поля
Если есть имя внешнего файла (external name), то оно записывается в поле (+0)
и завершается одним нулем. Если имя внешнего файла отсутствует, то поле (+0)
содержит 2 байта: первый из них - нуль, второй - любой (Clarion 3.1 пишет 1).
Пример:
(+0) (+n) (+m) (+m+2)
00 01 FILE_MEMO 00 10 27 01 00
Замечание: в поле (+m+2) программой, написанной на Clarion 3.1, записывается
значение 1, если memo-поле не имеет атрибута BINARY, и 2, если имеет. Clarion
Database Manager 3.1 пишет сюда всегда 1. Это поле ни программой, ни Database
Manager'ом не распознается (здесь может быть любое значение).
Для версии драйвера 2 (Clarion for Windows 1.5) байт атрибутов представляется
так:
0 0 0 0 0 1 0 1
│ │ L-- всегда 1
│ L---- 1 = есть атрибут BINARY
L------ 1 = это BLOB, 0 = это MEMO
Для BLOB длина memo-поля равна 0.
Замечание: при импорте структуры файла CfW 1.5 нигде не учитывает байт
атрибутов.
Описание ключа/индекса
----------------------
(+0) (+n) (+m) (+m+1) г============================┐
00 01 KEY1 00 21 02 00 │ АТРИБУТЫ KEY/INDEX │
? CSTRING BYTE USHORT │ │
Название Название Атрибуты Число │ 0 0 1 0 0 0 0 1 │
внешнего ключа полей │ L-+ │ │ L- DUP │
файла, в ключе │ 0 = KEY │ L--- OPT │
см.memo │ 1 = INDEX L-- NOCASE │
│ 2 = Dynamic index │
Дальше для кажного поля в ключе/индексе L============================-
следует запись
01 00 00 00
USHORT USHORT
Номер Атрибуты Атрибуты: 0 = ASCENDING
поля не 0 = DESCENDING
Для Dynamic index число полей всегда 0. Неизвестно, где хранятся данные
Dynamic index. Если атрибуты - не 0 (совершенно любое число), то поле считается
DESCENDING.
Замечание: если в поле (+0) содержится 00 00, то Clarion 3.1 при импорте
структуры считает, что у ключа/индекса есть внешнее имя "". Другие значения
второго байта (00 02, 00 03 и т.д.) распознаются правильно. У memo-полей
значение 00 00 распознается правильно.
Название таблицы
Последними записями в файле идут названия таблиц
(+0) (+1) (+n)
C0 0C 00 08 00 FE UNNAMED 00 00 00 01
BYTE USHORT USHORT BYTE STRING ULONG
Длина Длина Показывает, что Название Код файла
записи записи это название таблицы для этой
в упр. таблицы таблицы
странице
Байт по смещению (+0) показывает, что эта запись - название таблицы. Этим
байтом не должен начинаться ни один номер таблицы. Отчасти поэтому при
увеличении номера записи последний байт изменяется первым. Длина названия
таблицы вычисляется как длина_записи минус 5, а длина записи в упр.странице -
как длина_записи минус 4.
При создании таблицы функцией CREATE ее номер вычисляется как номер следующей
записи (берется номер последней записи из заголовка, увеличивается на 1,
записывается обратно в заголовок и считается номером таблицы).
Представление полей в ключах и индексах
ASCENDING
BYTE, STRING, Не меняются, GROUP рассматривается как STRING, даже
PICTURE, GROUP байты у входящих в нее чисел не переставляются.
CSTRING Неиспользуемые байты с правого края забиваются нулями.
PSTRING Байт длины не указывается, неиспользуемые символы
с правого края забиваются нулями, добавляется еще один
нуль с правого края, чтобы длина строки в ключе была
равна длине строки в записи таблицы.
USHORT, ULONG Байты переставляются задом наперед
SHORT, LONG Старший бит старшего байта инвертируется, байты
переставляются
REAL, SREAL Если число положительное, то старший бит старшего
байта инвертируется. В противном случае все биты
инвертируются. Байты переставляются.
DECIMAL Если число положительное, то старший бит старшего
байта инвертируется. В противном случае старший ниббл
становится равным 7, и инвертируются все остальные
биты
DESCENDING
BYTE, SHORT, LONG, USHORT, ULONG, REAL, SREAL, STRING, CSTRING,
PSTRING, DECIMAL То же, что и ACSENDING, только каждый бит
инвертируется
Замечание: если число DECIMAL отрицательное, то старший ниббл равен
8, остальные биты остаются без изменения. Фактически меняем
знак числа, а дальше строим как для ASCENDING.
Замечание: если число REAL/SREAL отрицательное, то просто переставляются
байты. Фактически меняем знак числа, а дальше строим как для
ASCENDING.
Примечания
Атрибуты RECLAIM, CREATE не изменяют содержимое файла, точно также, как и
процедуры LOCK, UNLOCK, HOLD, RELEASE.
Неясно до конца, для каких целей в заголовке файла используются две длины
файла. При обработке транзакции заголовок файла копируется сразу за последней
страницей файла (т.е. по адресу, указанному в поле (+6)). За заголовком идут
еще какие-то данные (страницы). В этом новом заголовке поле (+6) остается
прежним, а поле (+0Ah) показывает новую длину файла вместе с записанными
данными. Т.е. первая из длин - длина без "неиспользуемого" в конце файла
пространства.