info-mac@uw-beaver (info-mac) (11/16/84)
From: singer@harvard.ARPA (Andrew Singer) Concerning Macintosh Pascal I/O: I won't argue that I/O couldn't be faster, but understand that when you're reading and/or writing a character at a time there is considerably more overhead per byte than if you are reading and/or writing in larger chunks. This overhead includes not only the raw I/O overhead, but also the statement interpretation overhead on each Pascal statement to do the I/O and associated processing. In the case of reading MacPaint files, it is not really necessary to read a character at a time, as the following (crude) program demonstrates: { Program to read a picture from a MacPaint file and display a } { scaled-down image (a 4-to-1 reduction) of that picture in the } { Drawing window. } { } { Adapted from an algorithm given by Bill Atkinson in "The } { MacPaint Document Format," a 3-page writeup under "Macintosh } { Misc." in "Inside Macintosh." } program DisplayPicture; uses QuickDraw2; const UnPackBits = $A8D0; type Block = array[1..256] of Integer; var F : file of Block; I, J : Integer; Buffer : array[1..2] of Block; ScanLine : array[1..144] of Integer; SrcPtr, DstPtr : QDPtr; G : GrafPtr; B : BitMap; DstRect : Rect; MaskRgn : RgnHandle; begin ShowDrawing; GetPort(G); B.BaseAddr := @ScanLine; B.RowBytes := 72; SetRect(B.Bounds, 0, 0, 576, 4); SetRect(DstRect, 0, 0, 144, 1); MaskRgn := nil; Reset(F, OldFileName('Picture File*')); FrameRect(-1, -1, 181, 145); Get(F); for I := 1 to 2 do Read(F, Buffer[I]); SrcPtr := @Buffer; for I := 1 to 180 do begin DstPtr := @ScanLine; for J := 1 to 4 do begin InLineP(UnPackBits, @SrcPtr, @DstPtr, 72); if Ord(SrcPtr) > Ord(@Buffer) + 512 then begin Buffer[1] := Buffer[2]; if not Eof(F) then Read(F, Buffer[2]); SrcPtr := Pointer(Ord(SrcPtr) - 512) end end; CopyBits(B, G^.Portbits, B.Bounds, DstRect, SrcCopy, MaskRgn); OffsetRect(DstRect, 0, 1); end end. [For anyone out there who doesn't have a copy of the aforementioned writeup by Atkinson, I would be willing to explain this program in greater detail at a later date if any interest is expressed.] There is, unfortunately, a problem with I/O that makes life much worse when you're doing disk I/O. Suppose you have a program that reads from a disk file and writes to another file on the same disk (such as Stuart Reges' program). On reading a character, say, MacPascal calls the File Manager to read one byte from the file. The File Manager keeps a file buffer so that, unless the buffer is empty or invalid, it needs only to extract the requested byte from the buffer and return it. Unfortunately, MacPascal always lets the File Manager use its default buffer, which is a single 512-byte buffer shared by all open files on the same volume. Therefore, when a character is subsequently written out to another file on the same disk, the buffer containing the character initially read is flushed to make room for a write buffer for the output file. Likewise, upon reading the next character, the write buffer must be flushed (i.e., written out) to make room for a read buffer. And so on... The next release should no longer have this problem. For now, there are a couple of strategies for getting around this problem. First of all, if you are fortunate enough to have an external drive, reading from a file on one drive and writing to a file on the other will proceed more quickly (since they are on different volumes, they don't share buffers). A second strategy is, as indicated above, to read and write in larger chunks. Reading and writing a line at a time will perform significantly better than a character at a time. You can even do your own buffering on a block or multi-block basis, but don't expect to be able to declare a file of blocks (i.e., a suitably large array-type) and read a text file a whole block at a time - a text file is not likely to end exactly on a block boundary. [Reading by blocks for MacPaint files works because they *always* are a multiple of 512 bytes.] There is another common complaint about MacPascal I/O, and that is the speed (or lack thereof) of writing to the Text window. I won't explain in detail as to why this is so (a hint - the Text window is maintained with TE!). I will only comment that there is, again, much more overhead required to write character by character than if you write in larger chunks. Writing out entire lines at a time goes much faster. If you really want to blast text out to the screen, you can use WriteDraw to display text to the Drawing window. The "Browser" program on the release disk contains some sample code showing how to do this. Concerning CHAR vs. STRING type compatibility: Let me respond to Stuart Reges' comments via some quotes from the MacPascal reference manual: [Section 1.6] "A character-string represents a value of a string-type. As a string-type, a character-string is compatible not only with other string-types, but also char-types..." [Section 5.1.5.3] "...a char value is compatible with a string-type value, and when the two are compared, the char value is treated as a string-type value with length 1." I'll admit that the buggy if-statement presented by Reges is problematical. However, if you've extended Pascal to allow *real* strings (not just packed arrays of characters), then *not* allowing chars to be compatible with multi-character string is *really* problematical (have you ever tried to insert a CHAR into a STRING with UCSD Pascal or LisaPascal?). I suggest, rather, that the real problem is the narrowness of the proportionally-spaced space. Believe me, we are *seriously* considering monospacing quoted strings in the next release. Jon F. Hueras, Think Technologies, Inc. (singer@harvard)