Skip to content

Commit 8eccc45

Browse files
authored
Merge pull request #7 from formeo/feature/refactoring
parser add
2 parents 88213af + 376797f commit 8eccc45

File tree

3 files changed

+136
-2
lines changed

3 files changed

+136
-2
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,5 @@ __recovery/
6464

6565
# Castalia statistics file (since XE7 Castalia is distributed with Delphi)
6666
*.stat
67-
* .idea
68-
*
67+
.idea
68+

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,4 @@ MIT
8282
## Author
8383

8484
Gordienko Roman
85+

uRecordParser.pas

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
unit uRecordParser;
2+
3+
interface
4+
5+
uses
6+
Windows, SysUtils, uStructs, uDataPage;
7+
8+
const
9+
recDeleted = $01; // Çàïèñü ïîìå÷åíà êàê óäàë¸ííàÿ
10+
recModified = $02; // Çàïèñü áûëà ìîäèôèöèðîâàíà
11+
recHasSegment = $04; // Çàïèñü ñîäåðæèò ñåãìåíò (÷àñòü BLOB)
12+
recHasNulls = $08; // Çàïèñü ñîäåðæèò NULL çíà÷åíèÿ
13+
recVersioned = $10; // Çàïèñü âåðñèîíèðîâàíà (MVCC)
14+
recLocalMod = $20; // Ëîêàëüíàÿ ìîäèôèêàöèÿ
15+
recRemoteMod = $40; // Óäàë¸ííàÿ ìîäèôèêàöèÿ
16+
recLastForm = $80; // Ïîñëåäíÿÿ ôîðìà çàïèñè (â öåïî÷êå?)
17+
18+
19+
MIN_REC_HEADER_SIZE = 18;
20+
21+
type
22+
23+
TRecordHeader = record
24+
Flags: UShort; // rec_flags
25+
TransactionID: SLong; // rec_transaction
26+
BackPointer: SLong; // rec_back (SLONG - ýòî êîäèðîâàíèå PageNum è SlotNum)
27+
NextPointer: SLong; // rec_next (SLONG - êîäèðîâàíèå)
28+
Format: UShort; // rec_format
29+
DataLength: UShort; // rec_length
30+
end;
31+
32+
TParsedRecordFragment = record
33+
Header: TRecordHeader;
34+
Data: TBytes; // Ôàêòè÷åñêèå äàííûå çàïèñè (rec_data)
35+
OffsetInPage: Word; // Ñìåùåíèå ôðàãìåíòà â èñõîäíîé Data Page
36+
IsDeleted: Boolean; // Óäîáíîå ïîëå: ðåçóëüòàò ïðîâåðêè Flags
37+
// PageNum, SlotNum: Integer; // Ìîæíî äîáàâèòü, åñëè ðàñêîäèðóåì rec_back/next
38+
end;
39+
40+
41+
TParsedRecordFragmentArray = array of TParsedRecordFragment;
42+
43+
44+
TRecordParser = class
45+
private
46+
// Âíóòðåííÿÿ ôóíêöèÿ äëÿ èçâëå÷åíèÿ çàãîëîâêà èç áàéòîâ
47+
function GetRecordHeaderFromBytes(const RecordFragmentData: TBytes): TRecordHeader;
48+
public
49+
// Ïàðñèò îäèí ôðàãìåíò çàïèñè (TRecordFragment èç uDataPage)
50+
// Âõîä: RawFragmentData - áàéòû ôðàãìåíòà (TRecordFragment.Data)
51+
// FragmentOffset - ñìåùåíèå ýòîãî ôðàãìåíòà íà ñòðàíèöå (TRecordFragment.Offset)
52+
// Âîçâðàò: TParsedRecordFragment ñ ðàñïàðñåííûìè äàííûìè
53+
function ParseRecordFragment(const RawFragmentData: TBytes; FragmentOffset: Word): TParsedRecordFragment;
54+
55+
// Ïàðñèò ìàññèâ ôðàãìåíòîâ çàïèñåé (íàïðèìåð, ðåçóëüòàò uDataPage.ExtractDataFragments)
56+
// Âõîä: RawFragments - ìàññèâ TRecordFragment
57+
// Âîçâðàò: ìàññèâ TParsedRecordFragmentArray
58+
function ParseRecordFragments(const RawFragments: TRecordFragmentsArray): TParsedRecordFragmentArray;
59+
60+
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ: ïðîâåðÿåò, ïîìå÷åíà ëè âåðñèÿ êàê óäàë¸ííàÿ
61+
function IsRecordDeleted(const Header: TRecordHeader): Boolean;
62+
63+
// Âñïîìîãàòåëüíàÿ ôóíêöèÿ: ïðîâåðÿåò, âåðñèîíèðîâàíà ëè çàïèñü
64+
function IsRecordVersioned(const Header: TRecordHeader): Boolean;
65+
end;
66+
67+
implementation
68+
69+
function TRecordParser.GetRecordHeaderFromBytes(const RecordFragmentData: TBytes): TRecordHeader;
70+
var
71+
Offset: Integer;
72+
begin
73+
if Length(RecordFragmentData) < MIN_REC_HEADER_SIZE then
74+
raise Exception.Create('Record fragment data too small for record header.');
75+
76+
Offset := 0;
77+
Result.Flags := PWord(@RecordFragmentData[Offset])^;
78+
Inc(Offset, 2);
79+
80+
Result.TransactionID := PLongInt(@RecordFragmentData[Offset])^;
81+
Inc(Offset, 4);
82+
83+
Result.BackPointer := PLongInt(@RecordFragmentData[Offset])^;
84+
Inc(Offset, 4);
85+
86+
Result.NextPointer := PLongInt(@RecordFragmentData[Offset])^;
87+
Inc(Offset, 4);
88+
89+
Result.Format := PWord(@RecordFragmentData[Offset])^;
90+
Inc(Offset, 2);
91+
92+
Result.DataLength := PWord(@RecordFragmentData[Offset])^;
93+
Inc(Offset, 2);
94+
95+
// Ïðîâåðèì, ÷òî äëèíà äàííûõ íå âûõîäèò çà ïðåäåëû áóôåðà
96+
if Offset + Result.DataLength > Length(RecordFragmentData) then
97+
raise Exception.Create('Record header specifies data length exceeding fragment buffer size.');
98+
end;
99+
100+
function TRecordParser.ParseRecordFragment(const RawFragmentData: TBytes; FragmentOffset: Word): TParsedRecordFragment;
101+
begin
102+
Result.Header := GetRecordHeaderFromBytes(RawFragmentData);
103+
Result.OffsetInPage := FragmentOffset;
104+
Result.IsDeleted := IsRecordDeleted(Result.Header);
105+
106+
// Êîïèðóåì äàííûå çàïèñè (rec_data)
107+
SetLength(Result.Data, Result.Header.DataLength);
108+
if Result.Header.DataLength > 0 then
109+
Move(RawFragmentData[MIN_REC_HEADER_SIZE], Result.Data[0], Result.Header.DataLength);
110+
end;
111+
112+
function TRecordParser.ParseRecordFragments(const RawFragments: TRecordFragmentsArray): TParsedRecordFragmentArray;
113+
var
114+
i: Integer;
115+
begin
116+
SetLength(Result, Length(RawFragments));
117+
for i := 0 to High(RawFragments) do
118+
begin
119+
Result[i] := ParseRecordFragment(RawFragments[i].Data, RawFragments[i].Offset);
120+
end;
121+
end;
122+
123+
function TRecordParser.IsRecordDeleted(const Header: TRecordHeader): Boolean;
124+
begin
125+
Result := (Header.Flags and recDeleted) <> 0;
126+
end;
127+
128+
function TRecordParser.IsRecordVersioned(const Header: TRecordHeader): Boolean;
129+
begin
130+
Result := (Header.Flags and recVersioned) <> 0;
131+
end;
132+
133+
end.

0 commit comments

Comments
 (0)