13
13
import math
14
14
import struct
15
15
16
+ # Calculate these once globally rather than recomputing each time when needed.
17
+ __LINESEP = bytes ([ord (k ) for k in os .linesep ])
18
+ __LINESEP_WINDOWS = bytes ([ord (k ) for k in "\r \n " ])
19
+ __CR = ord ('\r ' )
20
+ __LF = ord ('\n ' )
21
+
16
22
def FileNameWildCardCompare (kString : str , kWildCard : str ) :
17
23
18
24
# [] are frequently used in OpenVMS filenames, but to fnmatch, indicate ranges.
@@ -31,12 +37,12 @@ def VMSWriteEOL(kFileMetaData : VMSBackupTypes.VMSFileParameters, bForceEOL : bo
31
37
32
38
if (kFileMetaData .nFilePointer >= kFileMetaData .nFileSize ) and kFileMetaData .bLFDetected :
33
39
34
- kFileMetaData .kFileHandle .write (bytes ([ ord ( k ) for k in os . linesep ]) )
40
+ kFileMetaData .kFileHandle .write (__LINESEP )
35
41
kFileMetaData .bLFDetected = False
36
42
37
43
elif bForceEOL :
38
44
39
- kFileMetaData .kFileHandle .write (bytes ([ ord ( k ) for k in os . linesep ]) )
45
+ kFileMetaData .kFileHandle .write (__LINESEP )
40
46
41
47
#end
42
48
@@ -49,63 +55,141 @@ def VMSWriteFile(kBlock : bytes, kFileMetaData : VMSBackupTypes.VMSFileParameter
49
55
bLastElementWasLFCR = False
50
56
bContainsLFCR = False
51
57
58
+ # TODO: This is covering up a bug elsewhere where 0 byte writes are somehow being passed onwards...
59
+ if 0 == nDataLength :
60
+ return
61
+ #end
62
+
52
63
if VMSBackupTypes .ExtractMode .ASCII == kFileMetaData .kMode :
53
64
54
- for i ,nByte in enumerate (kBlock [:nDataLength ]) :
65
+ # Note: To improve throughput, ASCII Mode doesn't iterate one character at a time, and
66
+ # instead finds all indices which contain the line seperators. This allows burst
67
+ # writing of everything in between. This does improve average write time at the
68
+ # expense of some additional complexity.
69
+ kIndicesOfInterest = [i for i ,k in enumerate (kBlock [:nDataLength ]) if k in __LINESEP_WINDOWS ]
70
+ if 0 == len (kIndicesOfInterest ) :
71
+
72
+ # This would fall into functionality associated with:
73
+ # (__LF != kBlock[nCRLFIndex]) and kFileMetaData.bLFDetected :
74
+ if kFileMetaData .bLFDetected :
75
+
76
+ # This is not seen as a valid EOL, so normalise it
77
+ kFileMetaData .kFileHandle .write (__LINESEP )
78
+
79
+ #end
80
+
81
+ kFileMetaData .bLFDetected = False
82
+ kFileMetaData .bLastElementWasLFCR = False
83
+
84
+ kFileMetaData .kFileHandle .write (kBlock [:nDataLength ])
85
+ return
86
+
87
+ #end
88
+
89
+ # File I/O Typically likes being performed in large bursts, therefore we buffer the data
90
+ # into RAM first.
91
+ kBytes = bytearray ()
92
+ nLastIndex = 0
93
+
94
+ for nCRLFIndex in kIndicesOfInterest :
95
+
96
+ if nCRLFIndex > nLastIndex :
97
+
98
+ # This would fall into functionality associated with:
99
+ # (__LF != kBlock[nCRLFIndex]) and kFileMetaData.bLFDetected :
100
+ if kFileMetaData .bLFDetected :
101
+
102
+ # This is not seen as a valid EOL, so normalise it
103
+ kBytes .extend (__LINESEP )
104
+
105
+ #end
106
+
107
+ kFileMetaData .bLFDetected = False
108
+ kFileMetaData .bLastElementWasLFCR = False
55
109
56
- if (nByte == ord ('\r ' )) and not kFileMetaData .bLFDetected :
110
+ kBytes .extend (kBlock [nLastIndex :nCRLFIndex ])
111
+
112
+ #end
113
+
114
+ if __CR == kBlock [nCRLFIndex ] and not kFileMetaData .bLFDetected :
57
115
58
116
# Do nothing whilst the EOL is assessed
59
117
kFileMetaData .bLFDetected = True
60
118
61
119
# Indicate this data package contains an LF/CR entry
62
120
bContainsLFCR = True
63
- bLastElementWasLFCR = (i + 1 ) == nDataLength
121
+ bLastElementWasLFCR = (nCRLFIndex + 1 ) == nDataLength
64
122
65
- elif (nByte == ord ( ' \n ' ) ) and kFileMetaData .bLFDetected :
123
+ elif (__LF == kBlock [ nCRLFIndex ] ) and kFileMetaData .bLFDetected :
66
124
67
125
# This file already contains standard EOL conventions
68
- kFileMetaData . kFileHandle . write ( bytes ([ ord ( k ) for k in os . linesep ]))
126
+ kBytes += __LINESEP
69
127
kFileMetaData .bLFDetected = False
70
128
71
129
# Indicate this data package contains an LF/CR entry
72
130
bContainsLFCR = True
73
- bLastElementWasLFCR = (i + 1 ) == nDataLength
131
+ bLastElementWasLFCR = (nCRLFIndex + 1 ) == nDataLength
74
132
75
- elif (nByte == ord ( ' \n ' ) ) and not kFileMetaData .bLFDetected :
133
+ elif (__LF == kBlock [ nCRLFIndex ] ) and not kFileMetaData .bLFDetected :
76
134
77
135
# This is not seen as a valid EOL, so normalise it
78
- kFileMetaData . kFileHandle . write ( bytes ([ ord ( k ) for k in os . linesep ]))
136
+ kBytes += __LINESEP
79
137
kFileMetaData .bLFDetected = False
80
138
81
139
# Indicate this data package contains an LF/CR entry
82
140
bContainsLFCR = True
83
- bLastElementWasLFCR = (i + 1 ) == nDataLength
141
+ bLastElementWasLFCR = (nCRLFIndex + 1 ) == nDataLength
84
142
85
- elif (nByte != ord ( ' \n ' ) ) and kFileMetaData .bLFDetected :
143
+ elif (__LF != kBlock [ nCRLFIndex ] ) and kFileMetaData .bLFDetected :
86
144
87
145
# This is not seen as a valid EOL, so normalise it
88
- kFileMetaData . kFileHandle . write ( bytes ([ ord ( k ) for k in os . linesep ]))
146
+ kBytes += __LINESEP
89
147
kFileMetaData .bLFDetected = False
90
148
91
149
# Indicate this data package contains an LF/CR entry
92
150
bContainsLFCR = True
93
151
94
152
# Note: This indicates the *previous* byte was an LF/CR therefore the current
95
153
# element isn't, hence no check to see if the Last Element is an LF/CR
96
-
154
+
97
155
# Output the current byte since it contained non-EOL data
98
- kFileMetaData . kFileHandle . write ( bytes ([ nByte ]) )
156
+ kBytes . append ( kBlock [ nCRLFIndex ] )
99
157
100
158
else :
101
159
102
- # Output the Current Byte
103
- kFileMetaData . kFileHandle . write ( bytes ([ nByte ]) )
160
+ # Shouldn't Occur
161
+ assert ( False )
104
162
105
163
#end
164
+
165
+ nLastIndex = nCRLFIndex + 1
106
166
107
167
#end
108
168
169
+ # Handle the Last Few Elements
170
+ if nLastIndex < nDataLength :
171
+
172
+ # This would fall into functionality associated with:
173
+ # (__LF != kBlock[nCRLFIndex]) and kFileMetaData.bLFDetected :
174
+ if kFileMetaData .bLFDetected :
175
+
176
+ # This is not seen as a valid EOL, so normalise it
177
+ kBytes .extend (__LINESEP )
178
+
179
+ #end
180
+
181
+ kFileMetaData .bLFDetected = False
182
+ kFileMetaData .bLastElementWasLFCR = False
183
+
184
+ kBytes .extend (kBlock [nLastIndex :nDataLength ])
185
+
186
+ #end
187
+
188
+ # Output the Buffered Data for Writing
189
+ if len (kBytes ) > 0 :
190
+ kFileMetaData .kFileHandle .write (kBytes )
191
+ #end
192
+
109
193
else :
110
194
111
195
kFileMetaData .kFileHandle .write (kBlock [:nDataLength ])
@@ -119,15 +203,16 @@ def VMSWriteFile(kBlock : bytes, kFileMetaData : VMSBackupTypes.VMSFileParameter
119
203
120
204
def CloseOpenFiles (kExtractStatus : dict ) :
121
205
122
- if None != kExtractStatus ["Current" ] :
123
- if None != kExtractStatus ["Current" ].kFileHandle :
124
- if kExtractStatus ["Current" ].nFilePointer != kExtractStatus ["Current" ].nFileSize :
125
- print (f"Warning: { kExtractStatus ["Current" ].kFileName } extracted { kExtractStatus ["Current" ].nFilePointer } /{ kExtractStatus ["Current" ].nFileSize } bytes." )
206
+ kFileMetaData = kExtractStatus ["Current" ]
207
+ if None != kFileMetaData :
208
+ if None != kFileMetaData .kFileHandle :
209
+ if kFileMetaData .nFilePointer != kFileMetaData .nFileSize :
210
+ print (f"Warning: { kFileMetaData .kFileName } extracted { kFileMetaData .nFilePointer } /{ kFileMetaData .nFileSize } bytes." )
126
211
#end
127
- # assert(kExtractStatus["Current"].nFilePointer == kExtractStatus["Current"].nFileSize)
128
- kExtractStatus ["Current" ].closeFile ()
129
- kExtractStatus ["Current" ] = None
212
+ # assert(kFileMetaData.nFilePointer == kFileMetaData.nFileSize)
213
+ kFileMetaData .closeFile ()
130
214
#end
215
+ kExtractStatus ["Current" ] = None
131
216
#end
132
217
133
218
#end
0 commit comments