There is another way to speed it up, too: buffer the output, so that instead of executing a write operation for every byte (which causes an entire cluster to be read in, modified, and written back out), you can execute one operation per cluster. The following [tt]
SUB[/tt]s do some simple buffering, and may be applicable to what you are doing. However, the design is simple; it cannot be intermixed with other output methods and can only handle one file at a time.
[tt]
SUB selectFileToBuffer(fileNumber%, bufferSize&)
SHARED bufferedFileNumber%, bufferedFileBuffer$, bufferedFileBufferOffset%
IF bufferedFileBufferOffset%
THEN flushBuffer
bufferedFileNumber% = fileNumber%
bufferedFileBuffer$ = ""
bufferedFileBuffer$ =
SPACE$(bufferSize&)
bufferedFileBufferOffset% = 0
END SUB
SUB writeBufferedChar(character$)
SHARED bufferedFileNumber%, bufferedFileBuffer$, bufferedFileBufferOffset%
bufferedFileBufferOffset% = bufferedFileBufferOffset% + 1
MID$(bufferedFileBuffer$, bufferedFileBufferOffset%, 1) = character$
IF bufferedFileBufferOffset% =
LEN(bufferedFileBuffer$)
THEN
PUT #bufferedFileNumber%, , bufferedFileBuffer$
bufferedFileOffset% = 0
END IF
END SUB
SUB writeBufferedByte(asciiValue%)
writeBufferedChar
CHR$(asciiValue%)
END SUB
SUB writeBufferedString(stringToWrite$)
SHARED bufferedFileNumber%, bufferedFileBuffer$, bufferedFileBufferOffset%
a$ = stringToWrite$
' Local copy
bytesLeft% =
LEN(a$)
DO WHILE bytesLeft%
bytesLeftInBuffer% =
LEN(bufferedFileBuffer$) - bufferedFileBufferOffset%
IF bytesLeftInBuffer% > bytesLeft
THEN EXIT DO
MID$(bufferedFileBuffer$, bufferedFileBufferOffset% + 1, bytesLeftInBuffer%) =
LEFT$(a$, bytesLeftInBuffer%)
PUT #bufferedFileNumber%, , bufferedFileBuffer$
a$ =
MID$(a$, bytesLeftInBuffer% + 1)
bytesLeft% = bytesLeft% - bytesLeftInBuffer%
bufferedFileBufferOffset% = 0
LOOP
MID$(bufferedFileBuffer$, bufferedFileBufferOffset% + 1, bytesLeft%) =
LEFT$(a$, bytesLeft%)
bufferedFileBufferOffset% = bufferedFileBufferOffset% + bytesLeft%
END SUB
SUB flushBuffer()
SHARED bufferedFileNumber%, bufferedFileBuffer$, bufferedFileBufferOffset%
IF bufferedFileBufferOffset%
THEN
usedBuffer$ =
LEFT$(bufferedFileBuffer$, bufferedFileBufferOffset%)
PUT #bufferedFileNumber%, , usedBuffer$
bufferedFileBufferOffset% = 0
END IF
END SUB
[/tt]
To start using these [tt]
SUB[/tt]s, call [tt]
SUB selectFileToBuffer[/tt] and pass it a file number opened by [tt]
OPEN[/tt] in [tt]
BINARY[/tt] mode, as well as a buffer size (4096 or 8192 should be about optimal -- smaller buffers cause degraded performance, but QB has trouble with strings larger than 8 kilobytes (it can do them, but the string space gets all fragmented and messy)).
Then, to output bytes or strings, call the appropriate output function (the routines remember the file number automatically). When you are done, call [tt]
SUB flushBuffer[/tt] before closing your file, otherwise some data will not be written to file and will be lost.
You can switch from your own output to this buffered output at any time (provided the file was opened [tt]
FOR BINARY[/tt]), and you can switch back to your own output code at any time by calling [tt]
SUB flushBuffer[/tt], however doing so very often would reduce the effectiveness of the buffer and degrade performance. When switching back from your routines to the buffered routines after the buffered routines have already been in use, you do not need to call [tt]
SUB selectFileToBuffer[/tt] again, as all the settings are remembered and the buffer is in a proper state from [tt]
SUB flushBuffer[/tt]. It should also be safe to switch the buffer from one file to another (it automatically flushes the buffer), but the same performance issue applies.
I will be shortly posting this as a FAQ.
Good luck
