-
Notifications
You must be signed in to change notification settings - Fork 22
/
zFPTFile.prw
358 lines (266 loc) · 9.09 KB
/
zFPTFile.prw
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#include 'protheus.ch'
#include 'fileio.ch'
#include "zLibNBin.ch"
/* ==========================================================
Classe ZFPTFILE
Autor Julio Wittwer
Data 01/2019
Descrição Classe de manutenção de arquivo DBF MEMO
Formato FTP ( FoxPro2 )
- Implementação de Leitura e Gravação
- Criação do FPT File usando Block Size de 16K
O processo de gravação é rápido, porem nao há mecanismo eficiente
Implementado para reaproveitamento de espaços vazios. Posteriormente
pode ser implementado uma estrutura, reservando alguns blocos,
para criar um mapa dos espaços livres para reaproveitamento
========================================================== */
CLASS ZFPTFILE
DATA oDBF // Objeto ZDBFFILE owner do MEMO
DATA cFileName // Nome do arquivo FPT
DATA nHMemo // Handler do arquivo
DATA nNextBlock // Proximo bloco para inserção de dados
DATA nBlockSize // Tamanho do bloco em bytes
DATA lExclusive // Arquivo aberto em modo exclusivo ?
DATA lCanWrite // Arquivo aberto para gravacao
METHOD NEW() // Construtor
METHOD CREATE() // Cria o arquivo
METHOD OPEN() // Abre o FPT
METHOD CLOSE() // Fecha o FPT
METHOD READMEMO() // Le um memo armazenado em um bloco
METHOD WRITEMEMO() // Insere ou atualiza um memo em um bloco
ENDCLASS
// ----------------------------------------------------------
// Construtor
// Recebe o objeto ZDBFFILE e o nome do arquivo FPT
METHOD NEW(_oDBF,_cFileName) CLASS ZFPTFILE
::oDBF := _oDBF
::cFileName := _cFileName
::nHMemo := -1
::nBlockSize := 16384
::nNextBlock := 1
::lExclusive := .F.
::lCanWrite := .F.
Return self
// ----------------------------------------------------------
// Criação do arquivo
// Cria um arquivo FPT vazio
METHOD CREATE() CLASS ZFPTFILE
Local nHFile, cHeader
Local cNextBlock, cBlockSize
Local cFiller1, cFiller2
// Cria o arquivo MEMO
nHFile := FCreate(::cFileName)
If nHFile == -1
Return .F.
Endif
// Cria o Header ( 512 bytes )
// Block Size = 16 K
/*
0 | Number of next | ^
1 | available block | |
2 | for appending data | Header
3 | (binary) *1 | |
|-----------------------| |
4 | ( Reserved ) | |
5 | | |
|-----------------------| |
6 | Size of blocks N *1 | |
7 | *2 | |
*/
// ----- Build 512 Bytes FPT Empty File Header -----
cNextBlock := NtoBin4(1) // Proximo bloco livre para escrita
cFiller1 := chr(0) + chr(0)
cBlockSize := NToBin2( ::nBlockSize ) // Tamanho do Bloco
cFiller2 := replicate( chr(0) , 504 )
// Monta o Header do arquivo
cHeader := cNextBlock + cFiller1 + cBlockSize +cFiller2
// Grava o header em disco
fWrite(nHFile,cHEader,512)
FClose(nHFile)
Return .T.
// ----------------------------------------------------------
// Abertura do arquivo FPT
// Recebe os mesmos modos de abertura do ZDBFFILE
METHOD OPEN(lExclusive,lCanWrite) CLASS ZFPTFILE
Local cBuffer := ''
Local nFMode := 0
If lExclusive = NIL ; lExclusive := .F. ; Endif
If lCanWrite = NIL ; lCanWrite := .F. ; Endif
::lExclusive := lExclusive
::lCanWrite := lCanWrite
If lExclusive
nFMode += FO_EXCLUSIVE
Else
nFMode += FO_SHARED
Endif
If lCanWrite
nFMode += FO_READWRITE
Else
nFMode += FO_READ
Endif
// Abre o arquivo MEMO
::nHMemo := FOpen(::cFileName,nFMode)
IF ::nHMemo == -1
Return .F.
Endif
// Le o Header do arquivo ( 512 bytes )
fSeek(::nHMemo,0)
fRead(::nHMemo,@cBuffer,512)
// Pega o numero do proximo bloco para append
::nNextBlock := Bin4toN( substr(cBuffer,1,4) )
// Le o Block Size do arquivo
::nBlockSize := Bin2ToN( substr(cBuffer,7,2) )
conout("")
conout("FPT Next Append Block ......: "+cValToChar(::nNextBlock))
conout("FPT Block Size ...... ......: "+cValToChar(::nBlockSize))
conout("")
Return .T.
// ----------------------------------------------------------
// Fecha o arquivo FPT
METHOD CLOSE() CLASS ZFPTFILE
IF ::nHMemo != -1
fClose(::nHMemo)
Endif
::nHMemo := -1
::nBlockSize := 16384
::nNextBlock := 1
::lExclusive := .F.
::lCanWrite := .F.
Return
// ----------------------------------------------------------
// Lê um campo memo armazenado em um Bloco
// Recebe o número do bloco como parâmetro
METHOD READMEMO(nBlock) CLASS ZFPTFILE
Local cMemo := ''
Local cBlock := space(8)
Local nFilePos := nBlock * ::nBlockSize
Local nRecType
// Leitura de MEMO em Arquivo FPT
// Offset 1-4 Record type
// Offset 5-8 Tamanho do registro
fSeek(::nHMemo , nFilePos)
fRead(::nHMemo,@cBlock,8)
// Pega o tipo do Registro ( Offset 0 size 4 )
nRecType := Bin4toN( substr(cBlock,1,4) )
/*
Record Type
00h Picture
01h Memo
02h Object
*/
If nRecType <> 1
UserException("Unsupported MEMO Record Type "+cValToChar(nRecType))
Endif
// Obrtém o tamanho do registro ( Offset 4 Size 4 )
nMemoSize := Bin4toN( substr(cBlock,5,4) )
// Lê o registro direto para a memória
fRead(::nHMemo,@cMemo,nMemoSize)
Return cMemo
// ------------------------------------------------------------
// Atualiza ou insere um valor em um campo memo
// Se nBlock = 0 , Conteudo novo
// Se nBlock > 0 , Conteudo j[a existente
//
// Release 20190109 - Tratar "limpeza" de campo.
// - Ignorar inserção de string vazia
METHOD WRITEMEMO( nBlock , cMemo ) CLASS ZFPTFILE
Local nTamFile
Local nFiller
Local cBuffer := ''
Local nFilePos
Local nMemoSize
Local nChuckSize
Local nUsedBlocks
Local nMaxMemoUpd
Local nFileSize
If ( !::lCanWrite )
UserException("ZFPTFILE::WRITEMEMO() FAILED - FILE OPENED FOR READ ONLY")
Endif
If Len(cMemo) == 0 .AND. nBlock == 0
// Atualização de campo memo vazia.
// Se o block é zero, estou inserindo, ignora a operação.
return 0
Endif
If nBlock > 0
// Eatou atualizando conteúdo
// verifica se cabe no blocco atual
// Se nao couber , usa um novo .
// Primeiro lê o tamanho do campo atual
// e quantos blocos ele usa
cBuffer := space(8)
nFilePos := nBlock * ::nBlockSize
fSeek(::nHMemo , nFilePos)
fRead(::nHMemo,@cBuffer,8)
nMemoSize := Bin4toN( substr(cBuffer,5,4) )
nChuckSize := nMemoSize + 8
// Calcula quantos blocos foram utilizados
nUsedBlocks := int( nChuckSize / ::nBlockSize )
IF nChuckSize > ( nUsedBlocks * ::nBlockSize)
nUsedBlocks++
Endif
// Calcula o maior campo memo que poderia reaproveitar
// o(s) bloco(s) usado(s), descontando os 8 bytes de controle
nMaxMemoUpd := (nUsedBlocks * ::nBlockSize) - 8
If len(cMemo) > nMaxMemoUpd
// Passou, nao dá pra reaproveitar.
// Zera o nBlock, para alocar um novo bloco
// Como se o conteudo estivesse sendo inserido agora
nBlock := 0
Else
// Cabe no mesmo bloco ... remonta o novo buffer
// e atualiza o campo.
// Mesmo que eu esteja atualizando o campo para uma string vazia
// eu mantenho o bloco alocado, para posterior reaproveitamento
nMemoSize := len(cMemo)
nChuckSize := nMemoSize + 8
cBuffer := NtoBin4( 01 ) // Tipo de registro = Memo
cBuffer += NtoBin4( nMemoSize )
cBuffer += cMemo
// Posiciona no inicio do bloco já usado
fSeek(::nHMemo , nFilePos)
fWrite(::nHMemo,cBuffer,nChuckSize)
Endif
Endif
If nBlock == 0
// Pega o tamanho do arquivo
nFileSize := fSeek(::nHMemo,0,2)
// Estou inserindo um conteudo em um campo memo ainda nao utilizado.
// Ou estou usando um novo bloco , pois o campo memo
// nao cabe no bloco anteriormente utilizado
// Utiliza o proximo bloco para inserção do Header
nTamFile := ::nNextBlock * ::nBlockSize
If nFileSize < nTamFile
// Se o ultimo bloco do arquivo ainda nao foi preenchido com um "Filler"
// até o inicio do proximo bloco , preenche agora
nFiller := nTamFile - nFileSize
fSeek(::nHMemo,0,2)
fWrite(::nHMemo , replicate( chr(0) , nFiller ) , nFiller )
Endif
// Monta o buffer para gravar
nMemoSize := len(cMemo)
cBuffer := NtoBin4( 01 ) // Tipo de registro = Memo
cBuffer += NtoBin4( nMemoSize )
cBuffer += cMemo
// Tamanho do campo memo no bloco
// soma 8 bytes ( tipo , tamanho )
nChuckSize := nMemoSize + 8
// Posiciona no proximo bloco livre e grava
nFilePos := ::nNextBlock * ::nBlockSize
fSeek(::nHMemo,nFilePos)
fWrite(::nHMemo,cBuffer,nChuckSize)
// Guarda o bloco usado para retorno
nBlock := ::nNextBlock
// Calcula quantos blocos foram utilizados
nUsedBlocks := int( nChuckSize / ::nBlockSize )
IF nChuckSize > ( nUsedBlocks * ::nBlockSize)
nUsedBlocks++
Endif
// Agora define o proximo bloco livre
// Soma no ultimo valor a quantidade de blocos usados
::nNextBlock += nUsedBlocks
// Agora atualiza no Header
fSeek(::nHMemo,0)
fWrite( ::nHMemo , nToBin4(::nNextBlock) , 4 )
Endif
// Retorna o numero do bloco usado para a operação
Return nBlock