From 498284cde6557b4dbd36844afe06e3ee3cca82e0 Mon Sep 17 00:00:00 2001 From: mazze Date: Thu, 17 Aug 2017 11:28:40 +0000 Subject: [PATCH] Start of port of AsyncIO library. Origin: http://aminet.net/package/dev/c/AsyncIO Doesn't come with an open source license. git-svn-id: https://svn.aros.org/svn/aros/trunk/contrib@54876 fb15a70f-31f2-0310-bbcc-cdcc74a49acc --- workbench/libs/asyncio/AsyncIO.guide | 606 +++++++++++++++++++++ workbench/libs/asyncio/AsyncIO.guide.info | Bin 0 -> 1715 bytes workbench/libs/asyncio/AsyncIO.readme | 31 ++ workbench/libs/asyncio/AsyncIO.readme.info | Bin 0 -> 1453 bytes workbench/libs/asyncio/asyncio.doc | 464 ++++++++++++++++ workbench/libs/asyncio/include/fd/asyncio_lib.fd | 17 + workbench/libs/asyncio/include/libraries/asyncio.h | 84 +++ workbench/libs/asyncio/src/AsyncLib.c | 39 ++ workbench/libs/asyncio/src/AsyncLibVer.c | 1 + workbench/libs/asyncio/src/CloseAsync.c | 48 ++ workbench/libs/asyncio/src/DMakeFile | 169 ++++++ workbench/libs/asyncio/src/FGetsLenAsync.c | 100 ++++ workbench/libs/asyncio/src/Lib.Def | 49 ++ workbench/libs/asyncio/src/Lib.c | 239 ++++++++ workbench/libs/asyncio/src/OpenAsync.c | 42 ++ workbench/libs/asyncio/src/OpenAsyncFH.c | 217 ++++++++ workbench/libs/asyncio/src/OpenAsyncFromFH.c | 21 + workbench/libs/asyncio/src/PeekAsync.c | 30 + workbench/libs/asyncio/src/ReadAsync.c | 59 ++ workbench/libs/asyncio/src/ReadCharAsync.c | 37 ++ workbench/libs/asyncio/src/ReadLineAsync.c | 76 +++ workbench/libs/asyncio/src/RecordAsyncFailure.c | 24 + workbench/libs/asyncio/src/RequeuePacket.c | 16 + workbench/libs/asyncio/src/Rev.config | 1 + workbench/libs/asyncio/src/Rev.h | 9 + workbench/libs/asyncio/src/SCOPTIONS | 19 + workbench/libs/asyncio/src/SeekAsync.c | 306 +++++++++++ workbench/libs/asyncio/src/SendPacket.c | 18 + workbench/libs/asyncio/src/WaitPacket.c | 70 +++ workbench/libs/asyncio/src/WriteAsync.c | 49 ++ workbench/libs/asyncio/src/WriteCharAsync.c | 34 ++ workbench/libs/asyncio/src/WriteLineAsync.c | 21 + workbench/libs/asyncio/src/async.h | 89 +++ workbench/libs/asyncio/src/asyncio.conf | 26 + workbench/libs/asyncio/src/asynciolibsr.h | 4 + workbench/libs/asyncio/src/mmakefile.src | 36 ++ 36 files changed, 3051 insertions(+) create mode 100644 workbench/libs/asyncio/AsyncIO.guide create mode 100644 workbench/libs/asyncio/AsyncIO.guide.info create mode 100644 workbench/libs/asyncio/AsyncIO.readme create mode 100644 workbench/libs/asyncio/AsyncIO.readme.info create mode 100644 workbench/libs/asyncio/asyncio.doc create mode 100644 workbench/libs/asyncio/include/fd/asyncio_lib.fd create mode 100644 workbench/libs/asyncio/include/libraries/asyncio.h create mode 100644 workbench/libs/asyncio/src/AsyncLib.c create mode 100644 workbench/libs/asyncio/src/AsyncLibVer.c create mode 100644 workbench/libs/asyncio/src/CloseAsync.c create mode 100644 workbench/libs/asyncio/src/DMakeFile create mode 100644 workbench/libs/asyncio/src/FGetsLenAsync.c create mode 100644 workbench/libs/asyncio/src/Lib.Def create mode 100644 workbench/libs/asyncio/src/Lib.c create mode 100644 workbench/libs/asyncio/src/OpenAsync.c create mode 100644 workbench/libs/asyncio/src/OpenAsyncFH.c create mode 100644 workbench/libs/asyncio/src/OpenAsyncFromFH.c create mode 100644 workbench/libs/asyncio/src/PeekAsync.c create mode 100644 workbench/libs/asyncio/src/ReadAsync.c create mode 100644 workbench/libs/asyncio/src/ReadCharAsync.c create mode 100644 workbench/libs/asyncio/src/ReadLineAsync.c create mode 100644 workbench/libs/asyncio/src/RecordAsyncFailure.c create mode 100644 workbench/libs/asyncio/src/RequeuePacket.c create mode 100644 workbench/libs/asyncio/src/Rev.config create mode 100644 workbench/libs/asyncio/src/Rev.h create mode 100644 workbench/libs/asyncio/src/SCOPTIONS create mode 100644 workbench/libs/asyncio/src/SeekAsync.c create mode 100644 workbench/libs/asyncio/src/SendPacket.c create mode 100644 workbench/libs/asyncio/src/WaitPacket.c create mode 100644 workbench/libs/asyncio/src/WriteAsync.c create mode 100644 workbench/libs/asyncio/src/WriteCharAsync.c create mode 100644 workbench/libs/asyncio/src/WriteLineAsync.c create mode 100644 workbench/libs/asyncio/src/async.h create mode 100644 workbench/libs/asyncio/src/asyncio.conf create mode 100644 workbench/libs/asyncio/src/asynciolibsr.h create mode 100644 workbench/libs/asyncio/src/mmakefile.src diff --git a/workbench/libs/asyncio/AsyncIO.guide b/workbench/libs/asyncio/AsyncIO.guide new file mode 100644 index 000000000..fd5415ce0 --- /dev/null +++ b/workbench/libs/asyncio/AsyncIO.guide @@ -0,0 +1,606 @@ +@Database AsyncIO.guide +@$VER: AsyncIO.guide 39.2 (2.9.97) +@Author Magnus Holmgren + +@Node Main AsyncIO.guide + + @{FG highlight}@{U}@{B}AsyncIO library@{UB}@{UU}@{FG text} + + Version 39.2 + + Original code by Martin Taillefer + updates by Magnus Holmgren + + +AsyncIO is a library providing double buffered asyncronous IO to files in a +DOS-like manner. + + + @{" Background " Link Background} + + @{" Contents " Link Contents} + + @{" Usage " Link Usage} + + @{" Technical notes " Link Notes} + + @{" History " Link History} + + @{" Authors " Link Authors} + +@EndNode + + + + +@Node Background AsyncIO.guide/Background + +@{U}@{B}Background@{UB}@{UU} + +Reading and writing data is crucial to most applications and is in many +cases a major bottleneck. Using AmigaDOS' sophisticated file system +architecture can help reduce, and sometimes eliminate, the time spent +waiting for IO to complete. This package offers a few small routines that +can greatly improve an application's IO performance. + +Normally, an application processes a file in a manner similar to the +following: + + 1 - Open the file + + 2 - Read some data + + 3 - Process data just read + + 4 - Repeat steps 2 and 3 until all data is processed + + 5 - Close file + +Although the above sequence works fine, it doesn't make full use of the +Amiga's multitasking abilities. Step 2 in the above can become a serious +bottleneck. Whenever the application needs some data by using the DOS +Read() function, AmigaDOS has to put that task to sleep, and initiate a +request to the file system to have it fetch the data. The file system then +starts up the disk hardware and reads the data. Once the data is read, the +application is woken up and can start processing the data just read. + +The point to note in the above paragraph is that when the file system is +reading data from disk, the application is asleep. Wouldn't it be nice if +the application could keep running while data is being fetched for it? + +Most Amiga hard drives make use of DMA (Direct Memory Access). DMA enables +a hard drive to transfer data to memory @{B}at the same time@{UB} as the CPU does +some work. This parallelism is what makes the set of accompanying routines +so efficient. They exploit the fact that data can be transfered to memory +while the application is busy processing other data. + +Using the asynchronous IO routines, an application's IO happens like this: + + 1 - Open the file, ask the file system to start reading ahead + + 2 - Read some data, ask the file system to read more data + + 3 - Process data + + 4 - Repeat steps 2 and 3 until all data is processed + + 5 - Close file + +Immediately after opening the file, a request is sent to the file system to +get it reading data in the background. By the time the application gets +around to reading the first byte of data, it is likely already in memory. +That means the application doesn't need to wait and can start processing +the data. As soon as the application starts processing data from the file, +a second request is sent out to the file system to fill up a second buffer. +Once the application is done processing the first buffer, it starts +processing the second one. When this happens, the file system starts +filling up the first buffer again with new data. This process continues +until all data has been read. + +The whole technique is known as "double-buffered asynchronous IO" since it +uses two buffers, and happens in the background (asynchronously). + +The set of functions presented below offers high-performance IO using the +technique described above. The interface is very similar to standard +AmigaDOS files. These routines enable full asynchronous read/write of any +file. + +@EndNode + + + + +@Node Contents AsyncIO.guide/Contents + +@{U}@{B}Contents@{UB}@{UU} + +The archive contains the following drawers: + +@{B}dlib@{UB} Link libraries for DICE. +@{B}emodules@{UB} "Include" files for E. These are currently not up to date. +@{B}include@{UB} Include files for C: + @{B}clib@{UB} Function prototypes. + @{B}diceclib@{UB} Special header for DICE 3.0 or higher, and is used + when the "-mi" option is used. Similar to the + proto file for SAS. Place them in DInclude:clib. + @{B}fd@{UB} Function descriptor, e.g. used by various + programming language tools (not really a C include + file). + @{B}libraries@{UB} General include file(s), for needed structures + and constants. + @{B}pragmas@{UB} Pragma file for DICE and SAS/C (and perhaps + StormC). + @{B}proto@{UB} Proto file for SAS/C. +@{B}lib@{UB} Link libraries for SAS/C. +@{B}libs@{UB} The shared library. +@{B}src@{UB} Source code for AsyncIO. + +@EndNode + + + + +@Node Usage AsyncIO.guide/Usage + +@{U}@{B}Usage@{UB}@{UU} + +AsyncIO is available both as a C link library, and as a shared library. +Their usage should be fairly straight forward. There are a couple of +details though, since some of the include files are used for both the +shared and the link library. I'll only adress the use of AsyncIO from C +here, as this is more or less the only language I use (in addition to some +assembler). + +@{U}@{B}Using the shared library@{UB}@{UU} + +When using the shared library, make sure ASIO_SHARED_LIB is defined before +you include any header file for AsyncIO. + +@{B}DICE@{UB}: Include . Then link with + dlib/asynciolib#?.lib, if you want the autoinit code, or you didn't + use the "-mi" option. If "-mi" is used, remeber to copy the file + "include/diceclib/asyncio_protos.h" to "dinclude:clib". + +@{B}SAS/C@{UB}: Include the file , and link with lib/asyncio.lib + if you need the autoinit code. The proto file defines + ASIO_SHARED_LIB for you, so this should be all you need to do. + Currently there is no glue code available. + +@{U}@{B}Using the link library@{UB}@{UU} + +@{B}DICE@{UB}: Simply define ASIO_REGARGS when you want the regards + version, and link with the proper library. If you use dcc for + linking, DICE should link with the right library. Otherwise you + will get linker errors. + +@{B}SAS/C@{UB}: Do @{I}not @{UI}include . Include + instead. It is very important that you link with the right library, + and define ASIO_REGARGS and link with "lib/asyncior.lib" when you + want the regargs version. + + Warning: If you link with the wrong library, the program will link + properly, but will show strange errors when run! The reason for + this "problem" is that I wanted to be able to use the regargs + version from assembler, without assuming which register contained + which argument, and then SAS/C does it this way (i.e. no difference + in function names, as would normally happen for register argument + functions). + +To use the "no externals" version of the link library, you first need to +compile it. ;) Then define ASIO_NOEXTERNALS, link with the new library, and +otherwise follow the instructions for the normal link libraries. ("No +externals" means that the code does not require any external data symbols, +like DOSBase or ExecBase, making the code purer. To do this, certain +function calls takes DOSBase and ExecBase as arguments.) + +To create the link libraries: + +@{B}DICE@{UB}: Type e.g. "lbmake asyncio s r e" and have dtmp: set to some + temporary space. This creates a regargs, smalldata version. + +@{B}SAS/C@{UB}: Type e.g. "dmake lib/asyncioer.lib" to get the regargs version. + +@{U}@{B}Notes@{UB}@{UU} + +Remember that all ASIO_#? symbols must be set before any AsyncIO header is +read. There is one exception: ASIO_SHARED_LIB does not need to be defined +before you include . + +I haven't personally tested all library versions in the makefile/lib.def. + +SAS/C users still need DMake (from DICE) to compile things. I'm used to +DMake, and haven't really used SMake. ;) + +The code was originally compiled with DICE. Current version is compiled +with SAS/C (as well). + +Getting the code to compile with other compilers (GCC, StormC, ... ) should +be fairly easy, since I've tried to keep all compiler specific things +within #defines in a header file, and generally tried to only use normal +ANSI/C. + +The DICE link libraries are created with the help of the LbMake utility in +the DICE package. The file Lib.Def contains the definitions for them. + +@EndNode + + + + +@Node Notes "AsyncIO.guide/Technical notes" + +@{U}@{B}Technical notes@{UB}@{UU} + +The purpose of this chapter is to describe the inner workings of the code. +It is partly for my benefit, so that I don't need to remember all details, +and partly for your benefit, if you want to try to find bugs, or maybe use +asyncronous code in places were AsyncIO as is does not provide the needed +functionality. + +The comments in the code do help, but here is the place to put everything +together, and place additional comments. + +Please note that the information described herein may change, and can not +be used in normal applications that use AsyncIO! It is intended for persons +dealing with the AsyncIO code itself (or derivates thereof). + + @{" The AsyncFile structure " Link AsyncFile} + (More to be added...) + +@EndNode + + + + +@Node AsyncFile "AsyncIO.guide/Technical notes/The AsyncFile structure" + +@{U}@{B}The AsyncFile structure@{UB}@{UU} + +@{B}af_File@{UB} (BPTR) +The DOS file handle. + +@{B}af_BlockSize@{UB} (ULONG) +Blocksize of the device the file resides on. + +@{B}af_Handler@{UB} (struct MsgPort *) +Handler port of the file system responsible for the file. + +@{B}af_BytesLeft@{UB} (LONG) +Number of bytes left in the buffer we are currently reading data from. + +@{B}af_BufferSize@{UB} (ULONG) +The size of one of the two IO buffers. + +@{B}af_Buffers[ 2 ]@{UB} (UBYTE *) +Pointers to the two IO buffers. + +@{B}af_Packet@{UB} (struct StandardPacket) +Packet structure used when communicating with the file system. + +@{B}af_PacketPort@{UB} (struct MsgPort) +Message port used to receive the packets sent to the file system. No +signal bit is allocated of this port; SIGB_SINGLE is used when needed. + +@{B}af_CurrentBuf@{UB} (ULONG) +Number of the buffer that is @{I}currently being filled @{UI}(by the file system), +or, if AS_WaitPacket() just has been called, the newly arrived buffer. It +does @{I}not @{UI}refer to the buffer we are currently copying data from. Can be +used to index the af_Buffers[] array. + +@{B}af_SeekOffset@{UB} (ULONG) +In order to keep the buffers block aligned while seeking in a read mode +file, this offset keeps track of the distance from the start of the buffer +to the position we should start reading the new data from. + +@{B}af_SysBase@{UB} (struct ExecBase *) +Pointer to SysBase. Only available if ASIO_NOEXTERNALS is defined. + +@{B}af_DOSBase@{UB} (struct DosLibrary *) +Pointer to DOSBase. Only available if ASIO_NOEXTERNALS is defined. + +@{B}af_PacketPending@{UB} (UBYTE) +If true, then the file system is currently filling a packet for us, and it +will eventually be waiting for us on the message port. + +@{B}af_ReadMode@{UB} (UBYTE) +If true, then the file is used for reading only. Otherwise the file is used +for writing only. + +@{B}af_CloseFH@{UB} (UBYTE) +If true, then CloseAsync() will close the file in af_File. If +OpenAsyncFromFH() is used to open the file, then this field will be false. + +@{B}af_SeekPastEOF@{UB} (UBYTE) +If true, then the last SeekAsync() tried to seek past EOF. SeekAsync() uses +this to allow the application to seek back to a valid position, and +continue reading from there. + +@{b}af_LastRes1@{ub} (ULONG) +@{b}af_LastBytesLeft@{ub} (ULONG) +These two stores the last af_BytesLeft and af_Packet.sp_Pkt.dp_Res1. This +is needed in order to be able to resume seeking after a seek past EOF. + +@EndNode + + + + +@Node History AsyncIO.guide/History + +@{U}@{B}History@{UB}@{UU} + +@{B}Release 1 - 23-Mar-94@{UB} + + * When seeking within the current read buffer, the wrong packet would + be sent out to be filled asynchronously. Depending on the data read + from the buffer, and how fast it was read, you could end up getting + incorrect data on subsequent ReadAsync() calls. + + * There was actually bufferSize*2 bytes allocated for IO buffers + instead of just bufferSize. This is fixed. So if you want the same + effective buffer size as before, you must double the value of the + bufferSize argument supplied to OpenAsync(). + + * MEMF_PUBLIC is now specified for the IO buffers. This is in support + of VM hacks such as GigaMem. + + * A Seek() call had the mode and offset parameters reversed. The code + worked, because both values were luckily always 0, but it was not + very clean. + + * Now uses a typedef for the AsyncFile structure, and enums for the + open modes and seek modes. + +@{B}Release 2 - 16-Feb-94@{UB} + + * SeekAsync() now consistently works. It was getting confused when + called multiple times in a row with no intervening IO. + + * WriteAsync() would produce garbage in the destination file if it + had to bring up a "Disk is full" requester, and the user freed some + room on the disk and selected "Retry". + +@{B}Release 3 - 12-Aug-95@{UB} - Changes from now on by Magnus Holmgren + + * SeekAsync() is now not unecessarely slow when doing some "kinds" of + read-mode seeks (typically when seeking after some small amount of + initial read). The problem was that it usually only considered the + newly arrived buffer, not both buffers. This could make it discard + both buffers, and restart reading, although one of the buffers + already had the needed data. + + Note that the kind of seeks that caused the above problem may still + seem to be somewhat slow, since the code must wait for both buffers + to be loaded. This cannot (easily) be avoided. + + * SeekAsync() doesn't cause the read buffer to contain garbage after + certain seeks any more. The problem was that ReadAsync() would read + from the wrong buffer (the one currently being loaded). This made + the files seem to contain garbage. This happened when the seek + location was within the newly arrived buffer. + + * The code package is now supplied as a link library, rather than a + single source module. The internal functions labeled "AS_#?" are + private and should not be called directly by the application. + + * A few minor "cosmetic" changes were done, to either make the code + more readable, or to make it slightly smaller. + + * Include file restructured a little (a public and a private part). + + * OpenAsync() now offers some new "options" (all from the AIFF code + by Olaf Barthel): + + * Opening a file from an already open filehandle is now possible. + + * A "no externals" version may be compiled, that doesn't require any + external variables to be available. + + * Each of the buffers will now be roughly bufferSize / 2 bytes large, + rather than bufferSize bytes (rel 6 note: Really from Taillefers + third release). + + * If there isn't enough memory for the requested buffer size, the + code will try with smaller buffers (still properly "aligned") + before giving up. + +@{B}Release 4 - 13-Sep-95@{UB} + + * Oops. Forgot to include the include/asyncio.h file. Maybe as well, + since I anyway had forgot to mention a few things in it.. ;) + + * Asyncio is now also available as a shared library (to be placed in + libs:). This means that a couple of new include files were added, + and a one include file was split up. + + The main reason for doing this was to simplify the use of it in + other languages (only need to "port" a few includes). I have no + other include files than those for (DICE) C at the moment, but feel + free to send me headers for other languages as well. + + Note: I have not yet verified that the SeekAsync function works, + but since read and write works just fine, I see no reason why + SeekAsync wouldn't work. ;) + + Also, I haven't really used it in programs much either (only some + testing), so the different defines and similar to use different + versions may be a bit clumsy to use (see above for more + information). Feel free to send me comments on how to improve this. + + * Changed so that a non-regargs version of the link library can be + created. To use the regargs version now, simply make sure that + ASIO_REGARGS is defined, and link with the proper link library. + + * Changed the name of the NOEXTERNALS define to ASIO_NOEXTERNALS. + Define this before you include if you want + to use that feature. Note that you need to create a proper link + library yourself! (With DICE, all you need to do is "LbMake asyncio + s r e" in the Src drawer.) + + * Modified OpenAsync() a little, to work around a problem when having + SnoopDos (with SendARexx active), MungWall and Enforcer running. + Not an asyncio bug as such, but.. ;) + +@{B}Release 6 - 10-Nov-95@{UB} + + * Bumped to version 6, since there was an "official" release 3 that I + was unaware of. However, most of the changes were in the AIFF + source. Thus, not much was "lost" due to this. (In fact, the + SeekAsync bugfix in the real release 3 was not correct. ;) + + * Sigh. The SeekAsync fix (to the performance problem) was buggy. :/ + I think I've got it right now. + + * asyncio.library bumed to version 37.1. + + * Cleaned up this documentation. + + * Fixed some bugs in the include files. + + * Made some fixes to the shared library. + +@{B}Release 7 - 7-Jan-96@{UB} + + * Files to use asyncio.library from E included. + + * Fixed yet some SeekAsync() problems. It could (still) get confused + when called multiple times in a row with no intervening IO. I + wonder if there are any holes left. ;) + + * Some more fixes to the shared library. + + * asyncio.library bumped to version 37.2. + + * Recompiled asyncio.library using SAS/C 6.56, and added general + SAS/C support. + + * Cleaned up parts of this doc. + +@{B}Release 8 - 28-Jan-97@{UB} + + * Yet another hole in SeekAsync() (hopefully) plugged. ;) Seeking in + a double-buffered file is tricky business it seems. Thanks to David + Eaves for finding it, and sending me a good report about it + (including source to reproduce it). + + * The problem was that when SeekAsync() really seeked past the end of + the file, it didn't notice, and would try to reload the "last" + buffer. + + * Finally finished up the ReadLineAsync() function. Only difference + from dos.library/FGets() should be the return value. I hope it + works properly, since I haven't tested it that much. ;) + + * Bumped the library version to 38, because of the new functions. + +@{B}Release 9 - 27-Apr-97@{UB} + + * Oops. The include/clib/asyncio_protos.h file wasn't quite correct, + causing problems when compiling with SAS/C and ASIO_REGARGS + defined. Also updated includes for StormC. Thanks to Alexander + Kazik who sent me the needed changes for StormC. + + * Michael B. Smith pointed out that ReadLineAsync() wasn't quite + FGets()-compatible. So he sent me his version, which was. I've now + added his version (with some minor changes by me), and changed the + implementation for ReadLineAsync() to first call the new function + FGetsLenAsync(), and then skip up to the LF char, for backward + compatibility. + + * Added PeekAsync(), to allow you to look ahead in the current buffer + without actually "reading" anything. Useful when reading from pipes + to e.g. find out the file type (needed in Visage ;). + + * Bumped the library version to 39, because of the new functions. + + * The autoopen code for SAS/C now uses __asiolibversion rather than + __oslibversion (in case of failure, __oslibversion is set to the + value of __asiolibversion, and the standard __autoopenfail is + called, in order to save code). The default value is 39, and any + external reference to __asiolibversion will cause the autoopen code + to be linked. + + * Ouch. The DMakeFile/lib.def files contained some laws. As a result, + the DICE linker libraries (in dlib/) were not updated at all. Now + there should be fresh libraries for both DICE and SAS/C. + + * Fixed a couple of typos in the autodocs, including the problem with + the enum arguments specified as (U)BYTE, when they need to be LONG + (some compilers assumes enums to be longs, unless told otherwise). + +@{B}Version 39.1 - 27-Jul-97@{UB} + + * Ouch. The "David Eaves" bug fix in SeekAsync() was wrong. Not only + did it not work as intended, the intention was all wrong. Should be + fixed now, I hope. ;) + + * Cleaned up the messy asyncio.doc file, and moved all non-autodoc + stuff to this AmigaGuide file. Also added some technical notes (not + complete yet). + + * Changed to "real" version numbers for the release archive. + + * Referencing __asiolibversion alone isn't enough to drag in autoinit + code any more. Should also make it easier for user programs to set + their own version now. ;) + + * Actually compiled a SAS/C version with ASIO_NOEXTERNALS defined. + PeekAsync() and FGetsLenAsync() needed a tweak because of this. The + generated link library seems to work as well. + + * Tweaked the DMakeFile (and fixed a few quirks), to create the + object files in various sub-drawers. + + * FGets(Len)Async() would never fill the entire read buffer, even when + it could (we're talking about one byte not used here! ;). + + * Tried to make the usage instructions a bit clearer. + +@{B}Version 39.2 - 2-Sep-97@{UB} + + * ReadLineAsync() didn't detect end-of-file if the last line wasn't + terminated by a line feed. + + * ReadLineAsync() could write one byte too much to the read buffer. + + * Tried to make SeekAsync() allow seeks after a seek past EOF (in + order to be more DOS-compatible). I hope I didn't break anything. ;) + + * Old bug: ReadCharAsync() and WriteCharAsync() could sometimes + manage to do their job successfully when they really should fail + (and return -1). + +@EndNode + + + + +@Node Authors AsyncIO.guide/Authors + +@{U}@{B}Authors@{UB}@{UU} + +@{B}Martin Taillefer@{UB} wrote the original code. + +@{B}Magnus Holmgren@{UB} (that's me ;) "took over" the development in 1995, since I +had found a couple of bugs in SeekAsync(). I contacted Martin Taillefer +about it, and asked if I could release new versions of AsyncIO, and he +didn't complain. + +@{B}Olaf Barthel@{UB} provided inspiration for some of the changes in release 3 +(one or two of them most likely came from Martin Taillefers third release, +which I was unaware of at the time). + +@{B}Michael B. Smith@{UB} wrote the FGetsAsync() and FGetsLenAsync() functions, +which I added after some (really) minor changes. + +Most of the changes I have done are marked in the sources with a comment +that starts with "MH:". + +If you want to contact me about something, feel free to send a message to: + + cmh@lls.se (internet) + "Magnus Holmgren", 2:203/512.10 (fidonet) + +Please note that I don't poll the fidonet address that often. ;) + +@EndNode diff --git a/workbench/libs/asyncio/AsyncIO.guide.info b/workbench/libs/asyncio/AsyncIO.guide.info new file mode 100644 index 0000000000000000000000000000000000000000..ea8a1707cf403dc6454fc3d43f950bc89bee2b3a GIT binary patch literal 1715 zcwVhj&u<$=6dtEZ9rcuB1sCsnQ%|f&-m$DIL6B_LKkBv8M71M|N}D=(LhN8s4j_)n zAHsoBVIdB!iUAW4axfs-P|M@rc7mFy95@mVsI5x6%-dbBo18c>(!Bk?@4cCMGyeSh z1acr47+pt`=w&p9&LD?#<;Ty^V0?r$8#_k-amEYC`R3TGoSo;>Y3I;{`I$s7p%LVm zK0Gr>xcK}SqC3$_%hfTI&lLG-$k>H0yK}u!+ z+b1WbVuMdhQe{k)mdV;~Jas{qn*xfT=|+z4)KGxvB$Z0CZraCS)yewMXohe-ZhH;5-9vSCj##!c z>Q?84OJKFG#eCBg*mTfj8#Hwv_pH>${V2t~3&bRO_s_T|1}!4qO84=Mg(=w+EVvHO z+Dq3s8TOz=ZNkDe994-i0*jDk&e&ODv{Zi#g%I?rR zR?~jB?pI3I8rA1+#coD>qM_T2@pP6s7&_L_(BP0eJL?Xv`_g|oC$0&g-$$gC=^GxTnVkOhoZ z8TQ5|Sx6O^DPvm%8BHwfF}k0{h{^aRy<9RVP&*-@yw0sNkk>>HD7k)88dOkoZNk5T zm~~%9q3AQ>uhHRAej8W<4xnq^zO$<5P-S@THLQkwagN zZ}R;2H{E4Lv_&+!?Y=60bWbGcoM|e!C(`N27PYgp$|iGjb-KIU^`Yqdn)wYK2%NZX zAa|p(*67n=SY0>7@YB|{qgaQD+Bd6CpMjl2kyT{;ljl?;(cc!S@AQ6!55T^S2tszhZ~$sTi?iaaJo<&ZZgnv3MIKvN{-9a1yS`ZRY< z30Y?-hOWs{Y%;-T2WB6NQWWVGsDg7SB-2u3k}06voamzOO+~dR%qZFVM22&fGL&4O zS8B?fMtTCuI|VY2LK!s3=#r0pHj(&Du~*KsGsNa3K41S(?~8d9Wqj#M%4iHiC|$= 0 for success. Indicates whether closing + the file worked or not. If the file was opened in read-mode, + then this call will always work. In case of error, + dos.library/IoErr() can give more information. + + SEE ALSO + OpenAsync(), dos.library/Close() + + asyncio/FGetsAsync asyncio/FGetsAsync + + NAME + FGetsAsync -- read a line from an async file, fgets-style. + + SYNOPSIS + buffer = FGetsAsync( file, buffer, size ); + d0 a0 a1 d0 + + APTR ReadLineAsync( struct AsyncFile *, APTR, LONG ); + + FUNCTION + This function reads a single line from an async file, stopping at + either a NEWLINE character or EOF. In either event, UP TO the + number of size specified bytes minus 1 will be copied into the + buffer. It returns the number of bytes put into the buffer, + excluding the null-termination. 0 indicates EOF, and -1 indicates + read error. + + If terminated by a newline, the newline WILL be the last character in + the buffer. The string read in IS null-terminated. + + INPUTS + file - opened file to read from, as obtained from OpenAsync() + buffer - buffer to read the line into. + size - size of the buffer, in bytes. + + RESULT + buffer - Pointer to buffer passed in, or NULL for immediate EOF or + for an error. If NULL is returned for EOF, then + dos.library/IoErr() returns 0. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadCharAsync(), WriteLineAsync(), + FGetsLenAsync(), ReadLineAsync(), dos.library/FGets() + + asyncio/FGetsLenAsync asyncio/FGetsLenAsync + + NAME + FGetsLenAsync -- read a line from an async file. + + SYNOPSIS + buffer = FGetsLenAsync( file, buffer, size, length ); + d0 a0 a1 d0 a2 + + APTR FGetsLenAsync( struct AsyncFile *, APTR, LONG, LONG * ); + + FUNCTION + This function reads a single line from an async file, stopping at + either a NEWLINE character or EOF. In either event, UP TO the + number of size specified bytes minus 1 will be copied into the + buffer. It returns the number of bytes put into the buffer, + excluding the null-termination. 0 indicates EOF, and -1 indicates + read error. + + If terminated by a newline, the newline WILL be the last character in + the buffer. The string read in IS null-terminated. + + INPUTS + file - opened file to read from, as obtained from OpenAsync() + buffer - buffer to read the line into. + size - size of the buffer, in bytes. + length - pointer to ULONG to hold the length of the line, excluding + the terminating null. + + RESULT + buffer - Pointer to buffer passed in, or NULL for immediate EOF or + for an error. If NULL is returned for EOF, then + dos.library/IoErr() returns 0. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadCharAsync(), WriteLineAsync(), + FGetsAsync(), ReadLineAsync(), dos.library/FGets() + + asyncio/OpenAsync asyncio/OpenAsync + + NAME + OpenAsync -- open a file for asynchronous IO. + + SYNOPSIS + file = OpenAsync( fileName, accessMode, bufferSize + d0 a0 d0 d1 + [, sysbase, dosbase ] ); + a1 a2 + + struct AsyncFile *OpenAsync( const STRPTR, LONG, LONG + [, struct ExecBase *, struct DosLibrary * ] ); + + file = OpenAsyncFromFH( handle, accessMode, bufferSize + a0 d0 d1 + [, sysbase, dosbase ] ); + a1 a2 + + struct AsyncFile *OpenAsyncFromFH( BPTR, LONG, LONG + [, struct ExecBase *, struct DosLibrary * ] ); + + FUNCTION + The named file is opened and an async file handle returned. If the + accessMode is MODE_READ, an existing file is opened for reading. + If accessMode is MODE_WRITE, a new file is created for writing. If + a file of the same name already exists, it is first deleted. If + accessMode is MODE_APPEND, an existing file is prepared for writing. + Data written is added to the end of the file. If the file does not + exists, it is created. + + 'fileName' is a filename and CANNOT be a window specification such as + CON: or RAW:, or "*" + + 'bufferSize' specifies the size of the IO buffer to use. There are + in fact two buffers allocated, each of roughly (bufferSize/2) bytes + in size. The actual buffer size use can vary slightly as the size + is rounded to speed up DMA. + + If the file cannot be opened for any reason, the value returned + will be NULL, and a secondary error code will be available by + calling the routine dos.library/IoErr(). + + INPUTS + name - name of the file to open, cannot be a window specification + accessMode - one of MODE_READ, MODE_WRITE, or MODE_APPEND + bufferSize - size of IO buffer to use. 8192 is recommended as it + provides very good performance for relatively little memory. + sysbase - Library base needed for the "no externals" version of the + library. + dosbase - Library base, as sysbase. + + RESULTS + file - an async file handle or NULL for failure. You should not access + the fields in the AsyncFile structure, these are private to the + async IO routines. In case of failure, dos.library/IoErr() can + give more information. + + NOTES (by MH) + Although stated that CON:, RAW:, or "*" cannot be used as the file + name, tests indicates that the 2.0+ "Console:" volume is safe to + use for writing (haven't tested reading). No guarantees though. + + Also note that there is no MODE_READWRITE for AsyncIO. You cannot + read and write to the same AsyncFile. + + SEE ALSO + CloseAsync(), dos.library/Open() + + asyncio/PeekAsync asyncio/PeekAsync + + NAME + PeekAsync -- read bytes from an async file without advancing file + pointer. + + SYNOPSIS + actualLength = PeekAsync( file, buffer, numBytes ); + d0 a0 a1 d0 + + FUNCTION + This function tries to read bytes of information from an opened + async file into the buffer given. 'numBytes' is the number of bytes + to read from the file. + + The read is done without advancing the file pointer, and the read + is limited to what is available in the current buffer (or the next + buffer, if the current buffer is empty). If the current buffer does + not contain 'numBytes' bytes, only the bytes left in the buffer is + read. + + Using PeekAsync() can remove the need to SeekAsync() back after some + ReadAsync() calls (making your read operations more pipe friendly). + + The value returned is the length of the information actually read. + So, when 'actualLength' is greater than zero, the value of + 'actualLength' is the the number of characters read. A value of + zero means that end-of-file has been reached. Errors are indicated + by a value of -1. + + INPUTS + file - opened file to read, as obtained from OpenAsync() + buffer - buffer where to put bytes read + numBytes - number of bytes to read into buffer + + RESULT + actualLength - actual number of bytes read, or -1 if an error. In + case of error, dos.library/IoErr() can give more information. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadCharAsync(), WriteAsync(), + dos.library/Read() + + + asyncio/ReadAsync asyncio/ReadAsync + + NAME + ReadAsync -- read bytes from an async file. + + SYNOPSIS + actualLength = ReadAsync( file, buffer, numBytes ); + d0 a0 a1 d0 + + LONG ReadAsync( struct AsyncFile *, APTR, LONG ); + + FUNCTION + This function reads bytes of information from an opened async file + into the buffer given. 'numBytes' is the number of bytes to read from + the file. + + The value returned is the length of the information actually read. + So, when 'actualLength' is greater than zero, the value of + 'actualLength' is the the number of characters read. Usually + ReadAsync() will try to fill up your buffer before returning. A value + of zero means that end-of-file has been reached. Errors are indicated + by a value of -1. + + INPUTS + file - opened file to read, as obtained from OpenAsync() + buffer - buffer where to put bytes read + numBytes - number of bytes to read into buffer + + RESULT + actualLength - actual number of bytes read, or -1 if an error. In + case of error, dos.library/IoErr() can give more information. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadCharAsync(), WriteAsync(), + dos.library/Read() + + asyncio/ReadCharAsync asyncio/ReadCharAsync + + NAME + ReadCharAsync -- read a single byte from an async file. + + SYNOPSIS + byte = ReadCharAsync( file ); + d0 a0 + + LONG ReadCharAsync( struct AsyncFile * ); + + FUNCTION + This function reads a single byte from an async file. The byte is + returned, or -1 if there was an error reading, or if the end-of-file + was reached. + + INPUTS + file - opened file to read from, as obtained from OpenAsync() + + RESULT + byte - the byte read, or -1 if no byte was read. In case of error, + dos.library/IoErr() can give more information. If IoErr() + returns 0, it means end-of-file was reached. Any other value + indicates an error. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadAsync(), WriteCharAsync() + dos.library/Read() + + asyncio/ReadLineAsync asyncio/ReadLineAsync + + NAME + ReadLineAsync -- read a line from an async file. + + SYNOPSIS + bytes = ReadLineAsync( file, buffer, size ); + d0 a0 a1 d0 + + LONG ReadLineAsync( struct AsyncFile *, APTR, ULONG ); + + FUNCTION + This function reads a single line from an async file, stopping at + either a NEWLINE character or EOF. In either event, UP TO the + number of size specified bytes minus 1 will be copied into the + buffer. It returns the number of bytes put into the buffer, + excluding the null-termination. 0 indicates EOF, and -1 indicates + read error. + + If the line in the file is longer than the buffer, the line will be + truncated (any newline will be present). Any data left in the file + upto the newline will be lost. + + If terminated by a newline, the newline WILL be the last character in + the buffer. The string read in IS null-terminated. + + INPUTS + file - opened file to read from, as obtained from OpenAsync() + buffer - buffer to read the line into. + size - size of the buffer, in bytes. + + RESULT + bytes - number of bytes read. In case of error (-1 is returned) + dos.library/IoErr() can give more information. EOF is indicated + by a return of 0. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadCharAsync(), FGetsAsync(), + WriteLineAsync(), dos.library/FGets() + + asyncio/SeekAsync asyncio/SeekAsync + + NAME + SeekAsync -- set the current position for reading or writing within + an async file. + + SYNOPSIS + oldPosition = SeekAsync( file, position, mode ); + d0 a0 d0 d1 + + LONG SeekAsync( struct AsyncFile *, LONG, LONG ); + + FUNCTION + SeekAsync() sets the read/write cursor for the file 'file' to the + position 'position'. This position is used by the various read/write + functions as the place to start reading or writing. The result is the + current absolute position in the file, or -1 if an error occurs, in + which case dos.library/IoErr() can be used to find more information. + 'mode' can be MODE_START, MODE_CURRENT or MODE_END. It is used to + specify the relative start position. For example, 20 from current + is a position 20 bytes forward from current, -20 is 20 bytes back + from current. + + To find out what the current position within a file is, simply seek + zero from current. + + INPUTS + file - an opened async file, as obtained from OpenAsync() + position - the place where to move the read/write cursor + mode - the mode for the position, one of MODE_START, MODE_CURRENT, + or MODE_END. + + RESULT + oldPosition - the previous position of the read/write cursor, or -1 + if an error occurs. In case of error, dos.library/IoErr() can + give more information. + + NOTES (by MH) + If you seek after having read only a few bytes, the function must + wait for both buffers to be loaded, before the seek can be done. + This can cause small delays. Note that the above case isn't the + only one, but the typical one. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadAsync(), WriteAsync(), + dos.library/Seek() + + asyncio/WriteAsync asyncio/WriteAsync + + NAME + WriteAsync -- write data to an async file. + + SYNOPSIS + actualLength = WriteAsync( file, buffer, numBytes ); + d0 a0 a1 d0 + + LONG WriteAsync( struct AsyncFile *, APTR, LONG ); + + FUNCTION + WriteAsync() writes bytes of data to an opened async file. 'numBytes' + indicates the number of bytes of data to be transferred. 'buffer' + points to the data to write. The value returned is the length of + information actually written. So, when 'actualLength' is greater + than zero, the value of 'actualLength' is the number of characters + written. Errors are indicated by a return value of -1. + + INPUTS + file - an opened file, as obtained from OpenAsync() + buffer - address of data to write + numBytes - number of bytes to write to the file + + RESULT + actualLength - number of bytes written, or -1 if error. In case + of error, dos.library/IoErr() can give more information. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadAsync(), WriteCharAsync(), + dos.library/Write() + + asyncio/WriteCharAsync asyncio/WriteCharAsync + + NAME + WriteCharAsync -- write a single byte to an async file. + + SYNOPSIS + result = WriteCharAsync( file, byte ); + d0 a0 d0 + + LONG WriteCharAsync( struct AsyncFile *, UBYTE ); + + FUNCTION + This function writes a single byte to an async file. + + INPUTS + file - an opened async file, as obtained from OpenAsync() + byte - byte of data to add to the file + + RESULT + result - 1 if the byte was written, -1 if there was an error. In + case of error, dos.library/IoErr() can give more information. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadAsync(), WriteAsync(), + dos.library/Write() + + asyncio/WriteLineAsync asyncio/WriteLineAsync + + NAME + WriteLineAsync -- write a line to an async file. + + SYNOPSIS + bytes = WriteLineAsync( file, buffer ); + + LONG WriteLineAsync( struct AsyncFile *, STRPTR ); + + FUNCTION + This function writes an unformatted string to an async file. No + newline is appended to the string. + + INPUTS + file - opened file to read from, as obtained from OpenAsync() + buffer - buffer to write to the file + + RESULT + bytes - number of bytes written, or -1 if there was an error. In + case of error, dos.library/IoErr() can give more information. + + SEE ALSO + OpenAsync(), CloseAsync(), ReadCharAsync(), WriteCharAsync(), + FGetsAsync(), FGetsLenAsync(), ReadLineAsync(), dos.library/FPuts() + diff --git a/workbench/libs/asyncio/include/fd/asyncio_lib.fd b/workbench/libs/asyncio/include/fd/asyncio_lib.fd new file mode 100644 index 000000000..6996c6f52 --- /dev/null +++ b/workbench/libs/asyncio/include/fd/asyncio_lib.fd @@ -0,0 +1,17 @@ +##base _AsyncIOBase +##bias 30 +##public +OpenAsync(fileName,mode,bufferSize)(a0,d0/d1) +OpenAsyncFromFH(handle,mode,bufferSize)(a0,d0/d1) +CloseAsync(file)(a0) +SeekAsync(file,position,mode)(a0,d0/d1) +ReadAsync(file,buffer,bytes)(a0/a1,d0) +WriteAsync(file,buffer,bytes)(a0/a1,d0) +ReadCharAsync(file)(a0) +WriteCharAsync(file,ch)(a0,d0) +ReadLineAsync(file,buf,bytes)(a0/a1,d0) +WriteLineAsync(file,buf)(a0/a1) +FGetsAsync(file,buf,bytes)(a0/a1,d0) +FGetsLenAsync(file,buf,bytes,length)(a0/a1,d0,a2) +PeekAsync(file,buffer,bytes)(a0/a1,d0) +##end diff --git a/workbench/libs/asyncio/include/libraries/asyncio.h b/workbench/libs/asyncio/include/libraries/asyncio.h new file mode 100644 index 000000000..9e4ffa3d9 --- /dev/null +++ b/workbench/libs/asyncio/include/libraries/asyncio.h @@ -0,0 +1,84 @@ +#ifndef LIBRARIES_ASYNCIO_H +#define LIBRARIES_ASYNCIO_H + + +/*****************************************************************************/ + + +#ifndef EXEC_TYPES_H +#include +#endif + +#ifndef EXEC_PORTS_H +#include +#endif + +#ifndef DOS_DOS_H +#include +#endif + +#ifndef DOS_DOSEXTENS_H +#include +#endif + + +/*****************************************************************************/ + + +/* This structure is public only by necessity, don't muck with it yourself, or + * you're looking for trouble + */ +typedef struct AsyncFile +{ + BPTR af_File; + ULONG af_BlockSize; + struct MsgPort *af_Handler; + UBYTE *af_Offset; + LONG af_BytesLeft; + ULONG af_BufferSize; + UBYTE *af_Buffers[2]; + struct StandardPacket af_Packet; + struct MsgPort af_PacketPort; + ULONG af_CurrentBuf; + ULONG af_SeekOffset; +#ifdef ASIO_NOEXTERNALS + struct ExecBase *af_SysBase; + struct DosLibrary *af_DOSBase; +#endif + UBYTE af_PacketPending; + UBYTE af_ReadMode; + UBYTE af_CloseFH; + UBYTE af_SeekPastEOF; + ULONG af_LastRes1; + ULONG af_LastBytesLeft; +} AsyncFile; + + +/*****************************************************************************/ + + +typedef enum OpenModes +{ + MODE_READ, /* read an existing file */ + MODE_WRITE, /* create a new file, delete existing file if needed */ + MODE_APPEND /* append to end of existing file, or create new */ +} OpenModes; + + +typedef enum SeekModes +{ + MODE_START = -1, /* relative to start of file */ + MODE_CURRENT, /* relative to current file position */ + MODE_END /* relative to end of file */ +} SeekModes; + + +/*****************************************************************************/ + + +#if defined(__SASC) && defined(ASIO_SHARED_LIB) +extern long __asiolibversion; /* Minimum version of asyncio.library */ +#endif + + +#endif /* ASYNCIO_H */ diff --git a/workbench/libs/asyncio/src/AsyncLib.c b/workbench/libs/asyncio/src/AsyncLib.c new file mode 100644 index 000000000..78c8268f9 --- /dev/null +++ b/workbench/libs/asyncio/src/AsyncLib.c @@ -0,0 +1,39 @@ +#include +#include + + +void __regargs __autoopenfail( char * ); + + +struct Library *AsyncIOBase; +static void *libbase; + +extern long __oslibversion; +extern long __asiolibversion; + + +LONG +_STI_110_OpenAsyncIO( VOID ) +{ + AsyncIOBase = libbase = OpenLibrary( "asyncio.library", __asiolibversion ); + + if( AsyncIOBase == NULL ) + { + __oslibversion = __asiolibversion; + __autoopenfail( "asyncio.library" ); + return( 1 ); + } + + return( 0 ); +} + + +VOID +_STD_110_CloseAsyncIO( VOID ) +{ + if( libbase ) + { + CloseLibrary( libbase ); + libbase = AsyncIOBase = NULL; + } +} diff --git a/workbench/libs/asyncio/src/AsyncLibVer.c b/workbench/libs/asyncio/src/AsyncLibVer.c new file mode 100644 index 000000000..8bc9baa2f --- /dev/null +++ b/workbench/libs/asyncio/src/AsyncLibVer.c @@ -0,0 +1 @@ +long __asiolibversion = 39; diff --git a/workbench/libs/asyncio/src/CloseAsync.c b/workbench/libs/asyncio/src/CloseAsync.c new file mode 100644 index 000000000..34bc9b0b5 --- /dev/null +++ b/workbench/libs/asyncio/src/CloseAsync.c @@ -0,0 +1,48 @@ +#include "async.h" + + +_LIBCALL LONG +CloseAsync( _REG( a0 ) AsyncFile *file ) +{ + LONG result; + + if( file ) + { +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase = file->af_SysBase; + struct DosLibrary *DOSBase = file->af_DOSBase; +#endif + result = AS_WaitPacket( file ); + + if( result >= 0 ) + { + if( !file->af_ReadMode ) + { + /* this will flush out any pending data in the write buffer */ + if( file->af_BufferSize > file->af_BytesLeft ) + { + result = Write( + file->af_File, + file->af_Buffers[ file->af_CurrentBuf ], + file->af_BufferSize - file->af_BytesLeft ); + } + } + } + + if( file->af_CloseFH ) + { + Close( file->af_File ); + } + + FreeVec(file); + } + else + { +#ifndef ASIO_NOEXTERNALS + SetIoErr( ERROR_INVALID_LOCK ); +#endif + result = -1; + } + + return( result ); +} diff --git a/workbench/libs/asyncio/src/DMakeFile b/workbench/libs/asyncio/src/DMakeFile new file mode 100644 index 000000000..0f84ef3ca --- /dev/null +++ b/workbench/libs/asyncio/src/DMakeFile @@ -0,0 +1,169 @@ +# +# DMakeFile for asyncio library. Both shared library and normal library models. +# +# +# Does not generate all flavors; only the basic ones: +# Normal arguments, smalldata +# Registered arguments, smalldata +# +# Should be easy to modify below to generate other flavors, if needed. +# + +SRC = CloseAsync.c OpenAsync.c OpenAsyncFH.c OpenAsyncFromFH.c PeekAsync.c \ + ReadAsync.c ReadCharAsync.c ReadLineAsync.c RecordAsyncFailure.c \ + RequeuePacket.c SeekAsync.c SendPacket.c WaitPacket.c WriteAsync.c \ + WriteCharAsync.c WriteLineAsync.c FGetsLenAsync.c + +OBJ = $(SRC:*.?:o/*.o) + +PRAGMA = /include/pragmas/asyncio_pragmas.h +FD = /include/fd/asyncio_lib.fd + +# Generate all "normal" flavors + +All : /libs/asyncio.library $(PRAGMA) \ + /dlib/asynciolibs.lib /dlib/asynciolibsr.lib /dlib/asyncios.lib /dlib/asynciosr.lib \ + /lib/asyncio.lib /lib/asyncior.lib + +asio : /libs/asyncio.library + +Rev.h : + Rev -s -i -p -t -b + +Lib.c : Rev.h + +# The only difference when making the shared library is that Lib.c is included +# (MUST be first), and that ASIO_SHARED_LIB is defined. It only affects another +# define in the (private) include file async.h + +/libs/asyncio.library : Lib.o $(OBJ) + Sc LINK TO %(left) %(right) + +Lib.o $(OBJ) : Lib.c $(SRC) + Sc DEF ASIO_SHARED_LIB UTILLIB OBJNAME %(left) %(right) + + +# Normal link libraries + +/lib/asyncio.lib : $(SRC:*.c:ol/*.o) AsyncLib.o AsyncLibVer.o + Sc OBJLIB %(left) %(right) + +$(SRC:*.c:ol/*.o) : $(SRC) + Sc PARAMS STACK OBJNAME %(left) %(right) + +/lib/asyncior.lib : $(SRC:*.c:olr/*.o) AsyncLib.o AsyncLibVer.o + Sc OBJLIB %(left) %(right) + +$(SRC:*.c:olr/*.o) AsyncLib.o AsyncLibVer.o : $(SRC) AsyncLib.c AsyncLibVer.c + Sc DEF ASIO_REGARGS OBJNAME %(left) %(right) + +$(OBJ) : async.h + + +# Keep the pragma file up to date + +$(PRAGMA) : $(FD) + FDTOPragma -o%(left) %(right) + + +# Link libraries for use with the shared version of the library. Only needed if +# the -mi option isn't used, or you use the autoinit stuff. + +/dlib/asynciolibs.lib : $(FD) + FDToLib -o%(left) %(right) -auto asyncio.library + +/dlib/asynciolibsr.lib : $(FD) + FDToLib -o%(left) %(right) -mr -hasynciolibsr.h -auto asyncio.library + +# Large data models of the above two libraries. Not generated per default. + +/dlib/asynciolibs.lib : $(FD) + FDToLib -o%(left) %(right) -auto asyncio.library -md + +/dlib/asynciolibsr.lib : $(FD) + FDToLib -o%(left) %(right) -mr -hasynciolibsr.h -auto asyncio.library -md + + +# Here the normal link library versions are generated. See lib.def for more information. + +/dlib/asyncios.lib : + LbMake asyncio s + +/dlib/asynciosr.lib : + LbMake asyncio s r + +# Other flavours, not generated per default + +/dlib/asynciol.lib : + LbMake asyncio l + +/dlib/asynciorl.lib : + LbMake asyncio l r + +/dlib/asyncioes.lib : + LbMake asyncio s e + +/dlib/asyncioesr.lib : + LbMake asyncio s r e + +/dlib/asyncioel.lib : + LbMake asyncio l e + +/dlib/asyncioelr.lib : + LbMake asyncio l r e + +/dlib/asynciolp.lib : + LbMake asyncio l p + +/dlib/asynciolrp.lib : + LbMake asyncio l r p + +/dlib/asyncioesp.lib : + LbMake asyncio s p e + +/dlib/asyncioesrp.lib : + LbMake asyncio s r p e + +/dlib/asyncioelp.lib : + LbMake asyncio l p e + +/dlib/asyncioelrp.lib : + LbMake asyncio l r p e + +/dlib/asynciosp.lib : + LbMake asyncio s p + +/dlib/asynciosrp.lib : + LbMake asyncio s r p + + +# SAS versions of some of the above + +/lib/asyncioer.lib : $(SRC:*.c:oler/*.o) AsyncLib.o + Sc OBJLIB %(left) %(right) + +$(SRC:*.c:oler/*.o) : $(SRC) + Sc DEF ASIO_NOEXTERNALS DEF ASIO_REGARGS OBJNAME %(left) %(right) + +/lib/asyncioe.lib : $(SRC:*.c:ole/*.o) AsyncLib.o + Sc OBJLIB %(left) %(right) + +$(SRC:*.c:ole/*.o) : $(SRC) + Sc PARAMS STACK DEF ASIO_NOEXTERNALS OBJNAME %(left) %(right) + + +# Some other "odd" SAS/C link library versions. "u" means that UtilityBase +# is needed, and "2" means 020+ compiled. + +/lib/asyncioru.lib : $(SRC:*.c:olru/*.o) AsyncLib.o + Sc OBJLIB %(left) %(right) + +$(SRC:*.c:olru/*.o) : $(SRC) + Sc UTILLIB DEF ASIO_REGARGS OBJNAME %(left) %(right) + +/lib/asyncioru2.lib : $(SRC:*.c:olru2/*.o) AsyncLib.o + Sc DEF ASIO_REGARGS OBJLIB %(left) %(right) + +$(SRC:*.c:olru2/*.o) : $(SRC) + Sc UTILLIB CPU 68020 DEF ASIO_REGARGS OBJNAME %(left) %(right) + diff --git a/workbench/libs/asyncio/src/FGetsLenAsync.c b/workbench/libs/asyncio/src/FGetsLenAsync.c new file mode 100644 index 000000000..ad1b9dfe3 --- /dev/null +++ b/workbench/libs/asyncio/src/FGetsLenAsync.c @@ -0,0 +1,100 @@ +#include "async.h" + + +_LIBCALL APTR +FGetsLenAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) APTR buf, + _REG( d0 ) LONG numBytes, _REG( a2 ) LONG *len ) +{ + UBYTE *p; + LONG length = 0; + + p = ( UBYTE * ) buf; + + /* Make room for \n and \0 */ + if( --numBytes <= 0 ) + { + /* Handle senseless cases */ + return( NULL ); + } + + while( TRUE ) + { + UBYTE *ptr; + LONG i, count; + + ptr = ( UBYTE * ) file->af_Offset; + + if( count = file->af_BytesLeft ) + { + count = MIN( count, numBytes ); + + for( i = 0; ( i < count ) && ( *ptr != '\n' ); ++i ) + { + *p++ = *ptr++; + } + + length += i; + + /* Check for valid EOL char */ + if( i < count ) + { + /* MH: Since i < count, and count <= numBytes, + * there _is_ room for \n\0. + */ + *p++ = '\n'; /* "Read" EOL char */ + ++i; + length += 1; + } + + file->af_BytesLeft -= i; + file->af_Offset += i; + + if( ( i >= numBytes ) || ( *( p - 1 ) == '\n' ) ) + { + /* MH: It is enough to break out of the loop. + * no need to "waste" code by making a special + * exit here. ;) + */ + break; + } + + numBytes -= i; + } + + /* MH: numBytes must be at least 1 here, so there is still room + * for \n\0, in case we read \n. + */ + + if( ReadAsync( file, p, 1 ) < 1 ) + { + break; + } + + --numBytes; + ++length; + + if( *p++ == '\n' ) + { + break; + } + } + + *p = '\0'; + *len = length; + + if( p == ( UBYTE * ) buf ) + { + return( NULL ); + } + + return( buf ); +} + + +_CALL APTR +FGetsAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) APTR buf, _REG( d0 ) LONG numBytes ) +{ + LONG len; + + return( FGetsLenAsync( file, buf, numBytes, &len ) ); +} diff --git a/workbench/libs/asyncio/src/Lib.Def b/workbench/libs/asyncio/src/Lib.Def new file mode 100644 index 000000000..5e03b2839 --- /dev/null +++ b/workbench/libs/asyncio/src/Lib.Def @@ -0,0 +1,49 @@ +# LIB.DEF +# +# LBMAKE definition file, use LBMAKE to generate any asyncio link library +# you wish. To use, DTMP: must be assigned to a little of semi-temporary +# space. The generated library will be placed in DLIB: +# +# Flag Description +# +# s generate small-data library +# r generate registered-args library +# p generate profiled library +# l generate large-data library +# e generate library that doesn't require external symbols. +# Valid in all combinations. NOTE: If profiling is used, +# then the profiler might drag in stuff that needs external +# symbols. I haven't tested this.. +# +# EXAMPLE: +# +# 1> LbMake asyncio l generate a large-data asyncio.lib +# +# 1> LbMake asyncio s p generate a small-data profiled asynciosp.lib +# + +@Type e -D ASIO_NOEXTERNALS +@Type s -ms -r -pr +@Type l -mD +@Type r -mRR -proto -mi -D ASIO_REGARGS +@Type p -prof -mi0 + +@Library asyncio /dlib/asyncio%s.lib + +@DefTree DTmp:asyncio_%s/ + +CloseAsync.c +asyncio +OpenAsync.c +asyncio +OpenAsyncFH.c +asyncio +OpenAsyncFromFH.c +asyncio +PeekAsync.c +asyncio +ReadAsync.c +asyncio +ReadCharAsync.c +asyncio +RecordAsyncFailure.c +asyncio +RequeuePacket.c +asyncio +SeekAsync.c +asyncio +SendPacket.c +asyncio +WaitPacket.c +asyncio +WriteAsync.c +asyncio +WriteCharAsync.c +asyncio +FGetsLenAsync.c +asyncio diff --git a/workbench/libs/asyncio/src/Lib.c b/workbench/libs/asyncio/src/Lib.c new file mode 100644 index 000000000..217625a98 --- /dev/null +++ b/workbench/libs/asyncio/src/Lib.c @@ -0,0 +1,239 @@ +/* Lib.c + * + * Basic Library Resource Handling + * + * This code is based on the example shared library, + * found on the DICE 3.0 disks. + * + * This code doesn't use any form of startup code (the example does). + * This is not something the novice C-programmer should attemt to do. + * You need to know what you are doing. :) + */ + +#include "async.h" +#include +#include +#include "rev.h" + +#include + +_LIBCALL struct Library *LibInit( _REG( d0 ) struct Library *, _REG( a0 ) APTR, _REG( a6 ) struct ExecBase * ); +_CALL struct Library *LibOpen( _REG( a6 ) struct Library * ); +_CALL APTR LibClose( _REG( a6 ) struct Library * ); +_LIBCALL APTR LibExpunge( _REG( a6 ) struct Library * ); + + +/* These variables will be zero, since "The memory used for bss + * blocks is zeroed by the loader when it is allocated" (quote + * from the AmigaDOS manual). + */ +struct Library *AsyncIOBase; /* Our library structure */ +struct ExecBase *SysBase; +struct Library *UtilityBase; +struct DosLibrary *DOSBase; +APTR SegList; + + +/* In case the user tries to run us, simply exit immediately. + * This code is also used for the reserved library function, + * that all libraries currently must have. + * + * Note that this function *must* be placed before any const + * data, or else the "exit if run" effect is lost (you are + * more likely to get a "crash if run" effect ;). + */ +LONG +LibReserved( VOID ) +{ + return( 0 ); +} + + +/* The functions the library should have. Unfortunately, we can't + * use the more compact format, using 16-bit relative entries, + * due to the compiler (bug or limitation ;). + */ + +static const APTR FuncTable[] = +{ + LibOpen, /* Standard library functions */ + LibClose, + LibExpunge, + LibReserved, + + OpenAsync, /* Our functions start here */ + OpenAsyncFromFH, + CloseAsync, + SeekAsync, + ReadAsync, + WriteAsync, + ReadCharAsync, + WriteCharAsync, + ReadLineAsync, + WriteLineAsync, + FGetsAsync, + FGetsLenAsync, + PeekAsync, + ( APTR ) -1 /* Terminate the table */ +}; + + +/* Table describing the library. We need this, since we use the + * autoinit feature. This means we don't need to call MakeLibrary() + * etc. in LibInit. + */ +static const ULONG InitTable[] = +{ + sizeof( struct Library ), /* Size of our library base, excluding jump table. We have no extra data here */ + ( ULONG ) FuncTable, /* The functions we have */ + NULL, /* InitStruct data. We init stuff ourselves instead */ + ( ULONG ) LibInit /* The library init function */ +}; + + +static const TEXT LibId[] = "asyncio.library "VERSION" (" DATE ")\r\n"; +static const TEXT LibName[] = "asyncio.library"; + + +/* And finaly the resident structure, used by InitResident(), + * in order to initialize everything. + */ +static const struct Resident RomTag = +{ + RTC_MATCHWORD, /* rt_MatchWord */ + &RomTag, /* rt_MatchTag */ + LibExpunge, /* rt_EndSkip */ + RTF_AUTOINIT, /* rt_Flags */ + VERNUM, /* rt_Version */ + NT_LIBRARY, /* rt_Type */ + 0, /* rt_Pri */ + LibName, /* rt_Name */ + LibId, /* rt_IDString */ + InitTable /* rt_Init */ +}; + + +/* This small function frees the library structure, + * and any other allocated resources. + */ +static VOID +FreeLib( struct Library *lib ) +{ + if( DOSBase ) /* In case we are loaded under kick 35 or earlier */ + { + CloseLibrary( ( struct Library * ) DOSBase ); + CloseLibrary( UtilityBase ); + } + + FreeMem( ( UBYTE * ) lib - lib->lib_NegSize, lib->lib_NegSize + lib->lib_PosSize ); +} + + +/* This function is called when the library is loaded, and the library base + * have been allocated. We are in a forbid section here, so don't do anything + * time-consuming, Wait() or similar. + * + * If all ok, return the library base. If anything went wrong, deallocate + * the library structure, and return NULL. + */ +_LIBCALL struct Library * +LibInit( _REG( d0 ) struct Library *lib, _REG( a0 ) APTR seglist, _REG( a6 ) struct ExecBase *sysBase ) +{ + SysBase = sysBase; + + /* Opening libraries in LibInit might not be a good idea under OS 1.x. + * Should be safe enough under 2.x and 3.x. + */ + if( ( DOSBase = ( struct DosLibrary * ) OpenLibrary( "dos.library", 37 ) ) && + ( UtilityBase = OpenLibrary( "utility.library", 37 ) ) ) + { + lib->lib_Node.ln_Type = NT_LIBRARY; + lib->lib_Node.ln_Pri = 0; + lib->lib_Node.ln_Name = LibName; + /* Request that checksum should be calculated */ + lib->lib_Flags = LIBF_CHANGED | LIBF_SUMUSED; + lib->lib_Version = VERNUM; + lib->lib_Revision = REVNUM; + lib->lib_IdString = ( APTR ) LibId; + SegList = seglist; + AsyncIOBase = lib; + } + else + { + FreeLib( lib ); + lib = NULL; + } + + return( lib ); +} + + +/* Open is given the library pointer. Either return the library pointer or NULL. + * Remove the delayed-expunge flag. Exec has Forbid() for us during the call. + * + * Since we don't refer to any smalldata here (directly or indirectly), + * we can safely skip "_LIBCALL", to save a few bytes. + */ +_CALL struct Library * +LibOpen( _REG( a6 ) struct Library *lib ) +{ + ++lib->lib_OpenCnt; + lib->lib_Flags &= ~LIBF_DELEXP; + return( lib ); +} + + +/* Close is given the library pointer. Be sure not to decrement the open + * count if already zero. If the open count is or becomes zero AND there + * is a LIBF_DELEXP, we expunge the library and return the seglist. + * Otherwise we return NULL. + * + * Note that this routine never sets LIBF_DELEXP on its own. + * + * Exec has Forbid() for us during the call. + * + * Since we don't refer to any smalldata here (directly. Indirectly we refer via + * LibExpunge, which is LibCall declared), we can safely skip "LibCall", to save + * a few bytes. + */ +_CALL APTR +LibClose( _REG( a6 ) struct Library *lib ) +{ + if( lib->lib_OpenCnt && --lib->lib_OpenCnt ) + { + return( NULL ); + } + + if( lib->lib_Flags & LIBF_DELEXP ) + { + return( LibExpunge( lib ) ); + } + + return( NULL ); +} + + +/* We expunge the library and return the Seglist ONLY if the open count is zero. + * If the open count is not zero we set the delayed-expunge flag and return NULL. + * + * Exec has Forbid() for us during the call. NOTE ALSO that Expunge might be + * called from the memory allocator and thus we CANNOT DO A Wait() or otherwise + * take a long time to complete (straight from RKM). + * + * RemLibrary() calls our expunge routine and would therefore freeze if we called + * it ourselves. LibExpunge() must remove the library itself as shown below. + */ +_LIBCALL APTR +LibExpunge( _REG( a6 ) struct Library *lib ) +{ + if( lib->lib_OpenCnt ) + { + lib->lib_Flags |= LIBF_DELEXP; + return( NULL ); + } + + Remove( &lib->lib_Node ); + FreeLib( lib ); + + return( SegList ); +} diff --git a/workbench/libs/asyncio/src/OpenAsync.c b/workbench/libs/asyncio/src/OpenAsync.c new file mode 100644 index 000000000..e42094bda --- /dev/null +++ b/workbench/libs/asyncio/src/OpenAsync.c @@ -0,0 +1,42 @@ +#include "async.h" + + +#ifdef ASIO_NOEXTERNALS +_LIBCALL AsyncFile * +OpenAsync( + _REG( a0 ) const STRPTR fileName, + _REG( d0 ) OpenModes mode, + _REG( d1 ) LONG bufferSize, + _REG( a1 ) struct ExecBase *SysBase, + _REG( a2 ) struct DosLibrary *DOSBase ) +#else +_LIBCALL AsyncFile * +OpenAsync( + _REG( a0 ) const STRPTR fileName, + _REG( d0 ) OpenModes mode, + _REG( d1 ) LONG bufferSize ) +#endif +{ + static const WORD PrivateOpenModes[] = + { + MODE_OLDFILE, MODE_NEWFILE, MODE_READWRITE + }; + BPTR handle; + AsyncFile *file = NULL; + + if( handle = Open( fileName, PrivateOpenModes[ mode ] ) ) + { +#ifdef ASIO_NOEXTERNALS + file = AS_OpenAsyncFH( handle, mode, bufferSize, TRUE, SysBase, DOSBase ); +#else + file = AS_OpenAsyncFH( handle, mode, bufferSize, TRUE ); +#endif + + if( !file ) + { + Close( handle ); + } + } + + return( file ); +} diff --git a/workbench/libs/asyncio/src/OpenAsyncFH.c b/workbench/libs/asyncio/src/OpenAsyncFH.c new file mode 100644 index 000000000..8a4741492 --- /dev/null +++ b/workbench/libs/asyncio/src/OpenAsyncFH.c @@ -0,0 +1,217 @@ +#include "async.h" + + +#ifdef ASIO_NOEXTERNALS +AsyncFile* +AS_OpenAsyncFH( + BPTR handle, + OpenModes mode, + LONG bufferSize, + BOOL closeIt, + struct ExecBase *SysBase, + struct DosLibrary *DOSBase ) +#else +AsyncFile * +AS_OpenAsyncFH( BPTR handle, OpenModes mode, LONG bufferSize, BOOL closeIt ) +#endif +{ + struct FileHandle *fh; + AsyncFile *file = NULL; + BPTR lock = NULL; + LONG blockSize, blockSize2; + D_S( struct InfoData, infoData ); + + if( mode == MODE_READ ) + { + if( handle ) + { + lock = DupLockFromFH( handle ); + } + } + else + { + if( mode == MODE_APPEND ) + { + /* in append mode, we open for writing, and then seek to the + * end of the file. That way, the initial write will happen at + * the end of the file, thus extending it + */ + + if( handle ) + { + if( Seek( handle, 0, OFFSET_END ) < 0 ) + { + if( closeIt ) + { + Close( handle ); + } + + handle = NULL; + } + } + } + + /* we want a lock on the same device as where the file is. We can't + * use DupLockFromFH() for a write-mode file though. So we get sneaky + * and get a lock on the parent of the file + */ + if( handle ) + { + lock = ParentOfFH( handle ); + } + } + + if( handle ) + { + /* if it was possible to obtain a lock on the same device as the + * file we're working on, get the block size of that device and + * round up our buffer size to be a multiple of the block size. + * This maximizes DMA efficiency. + */ + + blockSize = 512; + blockSize2 = 1024; + + if( lock ) + { + if( Info( lock, infoData ) ) + { + blockSize = infoData->id_BytesPerBlock; + blockSize2 = blockSize * 2; + bufferSize = ( ( bufferSize + blockSize2 - 1 ) / blockSize2 ) * blockSize2; + } + + UnLock(lock); + } + + /* now allocate the ASyncFile structure, as well as the read buffers. + * Add 15 bytes to the total size in order to allow for later + * quad-longword alignement of the buffers + */ + + for( ;; ) + { + if( file = AllocVec( sizeof( AsyncFile ) + bufferSize + 15, MEMF_PUBLIC | MEMF_ANY ) ) + { + break; + } + else + { + if( bufferSize > blockSize2 ) + { + bufferSize -= blockSize2; + } + else + { + break; + } + } + } + + if( file ) + { + file->af_File = handle; + file->af_ReadMode = ( mode == MODE_READ ); + file->af_BlockSize = blockSize; + file->af_CloseFH = closeIt; + + /* initialize the ASyncFile structure. We do as much as we can here, + * in order to avoid doing it in more critical sections + * + * Note how the two buffers used are quad-longword aligned. This + * helps performance on 68040 systems with copyback cache. Aligning + * the data avoids a nasty side-effect of the 040 caches on DMA. + * Not aligning the data causes the device driver to have to do + * some magic to avoid the cache problem. This magic will generally + * involve flushing the CPU caches. This is very costly on an 040. + * Aligning things avoids the need for magic, at the cost of at + * most 15 bytes of ram. + */ + + fh = BADDR( file->af_File ); + file->af_Handler = fh->fh_Type; + file->af_BufferSize = ( ULONG ) bufferSize / 2; + file->af_Buffers[ 0 ] = ( APTR ) ( ( ( ULONG ) file + sizeof( AsyncFile ) + 15 ) & 0xfffffff0 ); + file->af_Buffers[ 1 ] = file->af_Buffers[ 0 ] + file->af_BufferSize; + file->af_CurrentBuf = 0; + file->af_SeekOffset = 0; + file->af_PacketPending = FALSE; + file->af_SeekPastEOF = FALSE; +#ifdef ASIO_NOEXTERNALS + file->af_SysBase = SysBase; + file->af_DOSBase = DOSBase; +#endif + + /* this is the port used to get the packets we send out back. + * It is initialized to PA_IGNORE, which means that no signal is + * generated when a message comes in to the port. The signal bit + * number is initialized to SIGB_SINGLE, which is the special bit + * that can be used for one-shot signalling. The signal will never + * be set, since the port is of type PA_IGNORE. We'll change the + * type of the port later on to PA_SIGNAL whenever we need to wait + * for a message to come in. + * + * The trick used here avoids the need to allocate an extra signal + * bit for the port. It is quite efficient. + */ + + file->af_PacketPort.mp_MsgList.lh_Head = ( struct Node * ) &file->af_PacketPort.mp_MsgList.lh_Tail; + file->af_PacketPort.mp_MsgList.lh_Tail = NULL; + file->af_PacketPort.mp_MsgList.lh_TailPred = ( struct Node * ) &file->af_PacketPort.mp_MsgList.lh_Head; + file->af_PacketPort.mp_Node.ln_Type = NT_MSGPORT; + /* MH: Avoid problems with SnoopDos */ + file->af_PacketPort.mp_Node.ln_Name = NULL; + file->af_PacketPort.mp_Flags = PA_IGNORE; + file->af_PacketPort.mp_SigBit = SIGB_SINGLE; + file->af_PacketPort.mp_SigTask = FindTask( NULL ); + + file->af_Packet.sp_Pkt.dp_Link = &file->af_Packet.sp_Msg; + file->af_Packet.sp_Pkt.dp_Arg1 = fh->fh_Arg1; + file->af_Packet.sp_Pkt.dp_Arg3 = file->af_BufferSize; + file->af_Packet.sp_Pkt.dp_Res1 = 0; + file->af_Packet.sp_Pkt.dp_Res2 = 0; + file->af_Packet.sp_Msg.mn_Node.ln_Name = ( STRPTR ) &file->af_Packet.sp_Pkt; + file->af_Packet.sp_Msg.mn_Node.ln_Type = NT_MESSAGE; + file->af_Packet.sp_Msg.mn_Length = sizeof( struct StandardPacket ); + + if( mode == MODE_READ ) + { + /* if we are in read mode, send out the first read packet to + * the file system. While the application is getting ready to + * read data, the file system will happily fill in this buffer + * with DMA transfers, so that by the time the application + * needs the data, it will be in the buffer waiting + */ + + file->af_Packet.sp_Pkt.dp_Type = ACTION_READ; + file->af_BytesLeft = 0; + + /* MH: We set the offset to the buffer not being filled, in + * order to avoid special case code in SeekAsync. ReadAsync + * isn't affected by this, since af_BytesLeft == 0. + */ + file->af_Offset = file->af_Buffers[ 1 ]; + + if( file->af_Handler ) + { + AS_SendPacket( file, file->af_Buffers[ 0 ] ); + } + } + else + { + file->af_Packet.sp_Pkt.dp_Type = ACTION_WRITE; + file->af_BytesLeft = file->af_BufferSize; + file->af_Offset = file->af_Buffers[ 0 ]; + } + } + else + { + if( closeIt ) + { + Close( handle ); + } + } + } + + return( file ); +} diff --git a/workbench/libs/asyncio/src/OpenAsyncFromFH.c b/workbench/libs/asyncio/src/OpenAsyncFromFH.c new file mode 100644 index 000000000..2e28c5a60 --- /dev/null +++ b/workbench/libs/asyncio/src/OpenAsyncFromFH.c @@ -0,0 +1,21 @@ +#include "async.h" + + +#ifdef ASIO_NOEXTERNALS +_CALL AsyncFile * +OpenAsyncFromFH( + _REG( a0 ) BPTR handle, + _REG( d0 ) OpenModes mode, + _REG( d1 ) LONG bufferSize, + _REG( a1 ) struct ExecBase *SysBase, + _REG( a2 ) struct DosLibrary *DOSBase ) +{ + return( AS_OpenAsyncFH( handle, mode, bufferSize, FALSE, SysBase, DOSBase ) ); +} +#else +_CALL AsyncFile * +OpenAsyncFromFH( _REG( a0 ) BPTR handle, _REG( d0 ) OpenModes mode, _REG( d1 ) LONG bufferSize ) +{ + return( AS_OpenAsyncFH( handle, mode, bufferSize, FALSE ) ); +} +#endif diff --git a/workbench/libs/asyncio/src/PeekAsync.c b/workbench/libs/asyncio/src/PeekAsync.c new file mode 100644 index 000000000..4c6347832 --- /dev/null +++ b/workbench/libs/asyncio/src/PeekAsync.c @@ -0,0 +1,30 @@ +#include "async.h" + + +_LIBCALL LONG +PeekAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) APTR buffer, _REG( d0 ) LONG numBytes ) +{ +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase = file->af_SysBase; +#endif + + /* Try to fill a new buffer, if needed */ + if( !file->af_BytesLeft ) + { + LONG bytes; + + if( ( bytes = ReadAsync( file, &bytes, 1 ) ) <= 0 ) + { + return( bytes ); + } + + /* Unread byte */ + --file->af_Offset; + ++file->af_BytesLeft; + } + + /* Copy what we can */ + numBytes = MIN( numBytes, file->af_BytesLeft ); + CopyMem( file->af_Offset, buffer, numBytes ); + return( numBytes ); +} diff --git a/workbench/libs/asyncio/src/ReadAsync.c b/workbench/libs/asyncio/src/ReadAsync.c new file mode 100644 index 000000000..0896ebda4 --- /dev/null +++ b/workbench/libs/asyncio/src/ReadAsync.c @@ -0,0 +1,59 @@ +#include "async.h" + + +_LIBCALL LONG +ReadAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) APTR buffer, _REG( d0 ) LONG numBytes ) +{ +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase = file->af_SysBase; +#endif + LONG totalBytes = 0; + LONG bytesArrived; + + /* if we need more bytes than there are in the current buffer, enter the + * read loop + */ + + while( numBytes > file->af_BytesLeft ) + { + /* drain buffer */ + CopyMem( file->af_Offset, buffer, file->af_BytesLeft ); + + numBytes -= file->af_BytesLeft; + buffer = ( APTR ) ( ( ULONG ) buffer + file->af_BytesLeft ); + totalBytes += file->af_BytesLeft; + file->af_BytesLeft = 0; + + bytesArrived = AS_WaitPacket( file ); + + if( bytesArrived <= 0 ) + { + if( bytesArrived == 0 ) + { + return( totalBytes ); + } + + return( -1 ); + } + + /* ask that the buffer be filled */ + AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] ); + + /* in case we tried to seek past EOF */ + if( file->af_SeekOffset > bytesArrived ) + { + file->af_SeekOffset = bytesArrived; + } + + file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + file->af_SeekOffset; + file->af_CurrentBuf = 1 - file->af_CurrentBuf; + file->af_BytesLeft = bytesArrived - file->af_SeekOffset; + file->af_SeekOffset = 0; + } + + CopyMem( file->af_Offset, buffer, numBytes ); + file->af_BytesLeft -= numBytes; + file->af_Offset += numBytes; + + return( totalBytes + numBytes ); +} diff --git a/workbench/libs/asyncio/src/ReadCharAsync.c b/workbench/libs/asyncio/src/ReadCharAsync.c new file mode 100644 index 000000000..f78125174 --- /dev/null +++ b/workbench/libs/asyncio/src/ReadCharAsync.c @@ -0,0 +1,37 @@ +#include "async.h" + + +_CALL LONG +ReadCharAsync( _REG( a0 ) AsyncFile *file ) +{ + UBYTE ch; + + if( file->af_BytesLeft ) + { + /* if there is at least a byte left in the current buffer, get it + * directly. Also update all counters + */ + + ch = *file->af_Offset; + --file->af_BytesLeft; + ++file->af_Offset; + + return( ( LONG ) ch ); + } + + /* there were no characters in the current buffer, so call the main read + * routine. This has the effect of sending a request to the file system to + * have the current buffer refilled. After that request is done, the + * character is extracted for the alternate buffer, which at that point + * becomes the "current" buffer + */ + + if( ReadAsync( file, &ch, 1 ) > 0 ) + { + return( ( LONG ) ch ); + } + + /* We couldn't read above, so fail */ + + return( -1 ); +} diff --git a/workbench/libs/asyncio/src/ReadLineAsync.c b/workbench/libs/asyncio/src/ReadLineAsync.c new file mode 100644 index 000000000..cae7a8ff6 --- /dev/null +++ b/workbench/libs/asyncio/src/ReadLineAsync.c @@ -0,0 +1,76 @@ +#include "async.h" + + +_LIBCALL LONG +ReadLineAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) APTR buffer, _REG( d0 ) LONG bufSize ) +{ + LONG len; + + /* First read any data up to the LF or the buffer is full */ + if( FGetsLenAsync( file, buffer, bufSize, &len ) ) + { + UBYTE *end; + + end = ( ( UBYTE * ) buffer ) + len - 1; + + if( *end != '\n' ) + { + UBYTE ch = 0; + + /* We didn't reach EOF yet */ + while( TRUE ) + { + UBYTE *ptr; + LONG i, count; + + ptr = ( UBYTE * ) file->af_Offset; + + if( count = file->af_BytesLeft ) + { + /* Scan for LF char in buffer */ + for( i = 0; ( i < count ) && ( *ptr != '\n' ); ++i, ++ptr ) + { + } + + /* If i < count, then the loop above aborted + * due to LF char. + */ + if( i < count ) + { + ch = '\n'; + ++i; + } + + file->af_BytesLeft -= i; + file->af_Offset += i; + + if( i < count ) + { + /* All done */ + break; + } + } + + if( ReadAsync( file, &ch, 1 ) < 1 ) + { + break; + } + + if( ch == '\n' ) + { + /* All done */ + break; + } + } + + if( ch == '\n' ) + { + /* Overwrite last char with LF */ + *end++ = '\n'; + *end = '\0'; + } + } + } + + return( len ); +} diff --git a/workbench/libs/asyncio/src/RecordAsyncFailure.c b/workbench/libs/asyncio/src/RecordAsyncFailure.c new file mode 100644 index 000000000..3999fa147 --- /dev/null +++ b/workbench/libs/asyncio/src/RecordAsyncFailure.c @@ -0,0 +1,24 @@ +#include "async.h" + + +/* this function records a failure from a synchronous DOS call into the + * packet so that it gets picked up by the other IO routines in this module + */ +VOID +AS_RecordSyncFailure( AsyncFile *file ) +{ +#ifdef ASIO_NOEXTERNALS + struct DosLibrary *DOSBase = file->af_DOSBase; +#endif + + /* MH: Back up some values to make it possible to resume operation + * after seeks past EOF. + */ + file->af_LastRes1 = file->af_Packet.sp_Pkt.dp_Res1; + file->af_LastBytesLeft = file->af_BytesLeft; + + file->af_Packet.sp_Pkt.dp_Res1 = -1; + file->af_Packet.sp_Pkt.dp_Res2 = IoErr(); + /* MH: To make sure we can't read/write despite an error condition */ + file->af_BytesLeft = 0; +} diff --git a/workbench/libs/asyncio/src/RequeuePacket.c b/workbench/libs/asyncio/src/RequeuePacket.c new file mode 100644 index 000000000..055693b24 --- /dev/null +++ b/workbench/libs/asyncio/src/RequeuePacket.c @@ -0,0 +1,16 @@ +#include "async.h" + + +/* this function puts the packet back on the message list of our + * message port. + */ +VOID +AS_RequeuePacket( AsyncFile *file ) +{ +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase = file->af_SysBase; +#endif + + AddHead( &file->af_PacketPort.mp_MsgList, &file->af_Packet.sp_Msg.mn_Node ); + file->af_PacketPending = TRUE; +} diff --git a/workbench/libs/asyncio/src/Rev.config b/workbench/libs/asyncio/src/Rev.config new file mode 100644 index 000000000..7f7709d2b --- /dev/null +++ b/workbench/libs/asyncio/src/Rev.config @@ -0,0 +1 @@ +39.2.10 \ No newline at end of file diff --git a/workbench/libs/asyncio/src/Rev.h b/workbench/libs/asyncio/src/Rev.h new file mode 100644 index 000000000..44366049a --- /dev/null +++ b/workbench/libs/asyncio/src/Rev.h @@ -0,0 +1,9 @@ +#define VERSION "39.2" +#define DATE "9.11.97" +#define DAY 9 +#define MONTH 11 +#define YEAR 97 +#define VERNUM 39 +#define REVNUM 2 +#define SUBREVNUM 10 +#define DATESTAMP { 7252, 639, 299 } diff --git a/workbench/libs/asyncio/src/SCOPTIONS b/workbench/libs/asyncio/src/SCOPTIONS new file mode 100644 index 000000000..7393641a2 --- /dev/null +++ b/workbench/libs/asyncio/src/SCOPTIONS @@ -0,0 +1,19 @@ +PARAMETERS=REGISTERS +NOSTACKCHECK +STRINGMERGE +UNSIGNEDCHARS +ERRORREXX +NOMULTIPLEINCLUDES +STRUCTUREEQUIVALENCE +OPTIMIZE +STRINGSCONST +SMALLCODE +SMALLDATA +STRIPDEBUG +OPTIMIZERSCHEDULER +NOVERSION +NOICONS +MEMORYSIZE=HUGE +STRINGSECTION=CODE +IGNORE=104 +NOSTARTUP diff --git a/workbench/libs/asyncio/src/SeekAsync.c b/workbench/libs/asyncio/src/SeekAsync.c new file mode 100644 index 000000000..6ab08f2f3 --- /dev/null +++ b/workbench/libs/asyncio/src/SeekAsync.c @@ -0,0 +1,306 @@ +#include "async.h" + + +static ULONG +GetFileSize( AsyncFile *file, LONG *size ) +{ +#ifdef ASIO_NOEXTERNALS + struct DosLibrary *DOSBase = file->af_DOSBase; +#endif + D_S( struct FileInfoBlock, fib ); + + if( !ExamineFH( file->af_File, fib ) ) + { + AS_RecordSyncFailure( file ); + return( FALSE ); + } + + *size = fib->fib_Size; + return( TRUE ); +} + + +_LIBCALL LONG +SeekAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) LONG position, _REG( d1 ) SeekModes mode ) +{ +#ifdef ASIO_NOEXTERNALS + struct DosLibrary *DOSBase = file->af_DOSBase; +#endif + LONG current, target, roundTarget, filePos; + LONG minBuf, maxBuf, bytesArrived, diff; + LONG fileSize; + + bytesArrived = AS_WaitPacket( file ); + + /* MH: No packets can be pending here! */ + + if( bytesArrived < 0 ) + { + /* MH: Experimental: Try to allow "resume" of seeks past EOF. */ + + if( file->af_SeekPastEOF ) + { + /* MH: Restore saved values, to make resume possible */ + bytesArrived = file->af_LastRes1; + file->af_BytesLeft = file->af_LastBytesLeft; + } + else + { + return( -1 ); + } + } + + if( file->af_ReadMode ) + { + /* figure out what the actual file position is */ + filePos = Seek( file->af_File, 0, OFFSET_CURRENT ); + + if( filePos < 0 ) + { + AS_RecordSyncFailure( file ); + return( -1 ); + } + + /* figure out what the caller's file position is */ + current = filePos - ( file->af_BytesLeft + bytesArrived ) + file->af_SeekOffset; + + /* MH: We can't clear af_SeekOffset here. If another seek is done + * directly after this one, it would mean that we will both return + * the wrong position, and start reading from the wrong position. + */ + /* file->af_SeekOffset = 0; */ + + /* figure out the absolute offset within the file where we must seek to */ + if( mode == MODE_CURRENT ) + { + target = current + position; + } + else if( mode == MODE_START ) + { + target = position; + } + else /* if( mode == MODE_END ) */ + { + if( !GetFileSize( file, &fileSize ) ) + { + return( -1 ); + } + + target = fileSize + position; + } + + /* MH: Here we must be able to handle two different situations: + * 1) A seek directly after having dropped both buffers, and started + * refilling (typical case: File open). + * 2) Other seeks (typical case: A seek after some initial reading). + * + * We need to subtract with "af_Buffers[ 1 - file->af_CurrentBuf ]", + * as af_CurrentBuf refers to the *arrived* buffer, not the one we're + * currently reading from (and af_Offset points into the buffer we're + * reading from)! + * + * In case 1, there will be only one packet received. af_CurrentBuf + * will be zero, and refers to the newly arrived buffer (as it + * should). For proper behaviour in the minBuf calculation, we have + * set af_Offset to point to af_Buffers[ 1 ], when starting reading + * to empty buffers. That way wee need no special case code here. + * ReadAsync() can handle this, as af_BytesLeft == 0 in that case. + */ + + /* figure out what range of the file is currently in our buffers */ + minBuf = current - ( LONG ) ( file->af_Offset - file->af_Buffers[ 1 - file->af_CurrentBuf ] ); + maxBuf = current + file->af_BytesLeft + bytesArrived; /* WARNING: this is one too big */ + + diff = target - current; + +#ifdef DO_SOME_DEBUG + Printf( "Target: %ld, minBuf: %ld, maxBuf: %ld, current: %ld, diff: %ld, bytesLeft: %ld\n", + target, minBuf, maxBuf, current, diff, file->af_BytesLeft ); +#endif + + if( ( target < minBuf ) || ( target >= maxBuf ) ) + { + /* the target seek location isn't currently in our buffers, so + * move the actual file pointer to the desired location, and then + * restart the async read thing... + */ + + if( target >= maxBuf ) + { + /* MH: There's a fair chance that we really tried to seek + * past EOF. In order to tell for sure, we need to compare + * the seek target with the file size. The roundTarget may + * be before real EOF, so the "real" Seek() call might + * not notice any problems. + */ + if( !GetFileSize( file, &fileSize ) ) + { + return( -1 ); + } + + if( target > fileSize ) + { + /* MH: Experimental: Try to allow "resume" of + * seeks past EOF. + */ + file->af_SeekPastEOF = TRUE; + + SetIoErr( ERROR_SEEK_ERROR ); + AS_RecordSyncFailure( file ); + return( -1 ); + } + } + + /* this is to keep our file reading block-aligned on the device. + * block-aligned reads are generally quite a bit faster, so it is + * worth the trouble to keep things aligned + */ + roundTarget = ( target / file->af_BlockSize ) * file->af_BlockSize; + + if( Seek( file->af_File, roundTarget - filePos, OFFSET_CURRENT ) < 0 ) + { + AS_RecordSyncFailure( file ); + return( -1 ); + } + + AS_SendPacket( file, file->af_Buffers[ 0 ] ); + + file->af_SeekOffset = target - roundTarget; + file->af_BytesLeft = 0; + file->af_CurrentBuf = 0; + + /* MH: We set af_Offset to the buffer not being filled, to be able to + * handle a new seek directly after this one (see above; minBuf + * calculation). If we start reading after this seek, ReadAsync() + * will handle everything correctly, as af_BytesLeft == 0. + */ + file->af_Offset = file->af_Buffers[ 1 ]; + } + else if( ( target < current ) || ( diff <= file->af_BytesLeft ) ) + { + /* one of the two following things is true: + * + * 1. The target seek location is within the current read buffer, + * but before the current location within the buffer. Move back + * within the buffer and pretend we never got the pending packet, + * just to make life easier, and faster, in the read routine. + * + * 2. The target seek location is ahead within the current + * read buffer. Advance to that location. As above, pretend to + * have never received the pending packet. + */ + + AS_RequeuePacket( file ); + + file->af_BytesLeft -= diff; + file->af_Offset += diff; + + /* MH: We don't need to clear the seek offset here, since + * if we get here, we must have read some data from the current + * buffer, and af_SeekOffset will be zero then (done by + * ReadAsync()). + */ + + /* MH: If we're recovering from seek past EOF, restore some + * values here. + */ + if( file->af_SeekPastEOF ) + { + file->af_Packet.sp_Pkt.dp_Res1 = file->af_LastRes1; + } + } + else + { + /* at this point, we know the target seek location is within + * the buffer filled in by the packet that we just received + * at the start of this function. Throw away all the bytes in the + * current buffer, send a packet out to get the async thing going + * again, readjust buffer pointers to the seek location, and return + * with a grin on your face... :-) + */ + + /* MH: Don't refill the buffer we just got, but the other one! */ + AS_SendPacket( file, file->af_Buffers[ 1 - file->af_CurrentBuf ] ); + + /* MH: Account for bytes left in buffer we drop *and* the af_SeekOffset. + */ + diff -= file->af_BytesLeft - file->af_SeekOffset; + + /* MH: Set the offset into the current (newly arrived) buffer */ + file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ] + diff; + file->af_BytesLeft = bytesArrived - diff; + + /* MH: We need to clear the seek offset here, since we can't do it above. + */ + file->af_SeekOffset = 0; + + /* MH: This "buffer switching" is important to do. It wasn't done! + * This explains the errors one could encounter now and then. + * The AS_SendPacket() call above is not the cause, and *is* correct. + */ + file->af_CurrentBuf = 1 - file->af_CurrentBuf; + } + } + else + { + /* flush the buffers */ + if( file->af_BufferSize > file->af_BytesLeft ) + { + if( Write( + file->af_File, + file->af_Buffers[ file->af_CurrentBuf ], + file->af_BufferSize - file->af_BytesLeft ) < 0 ) + { + AS_RecordSyncFailure( file ); + return( -1 ); + } + } + + /* this will unfortunately generally result in non block-aligned file + * access. We could be sneaky and try to resync our file pos at a + * later time, but we won't bother. Seeking in write-only files is + * relatively rare (except when writing IFF files with unknown chunk + * sizes, where the chunk size has to be written after the chunk data) + */ + + /* MH: Ideas on how to improve the above (not tested, since I don't need + * the SeekAsync for writing in any of my programs at the moment! ;): + * + * Add a new field to the AsyncFile struct. af_WriteOffset or something like + * that (af_SeekOffset can probably be used). Like in the read case, we + * calculate a roundTarget, but we don't seek to that (but rather to the + * "absolute" position), and save the difference in the struct. af_BytesLeft + * and af_Offset are adjusted to point into the "middle" of the buffer, + * where the write will occur. Writes then needs some minor changes: + * Instead of simply writing the buffer from the start, we add the offset + * (saved above) to the buffer base, and write the partial buffer. The + * offset is then cleared. Voila: The file is still block-aligned, at the + * price of some non-optimal buffer usage. + * + * Problem: As it is now, Arg3 in the packet is always set to the buffer size. + * With the above fix, this would have to be updated for each SendPacket (i.e. + * a new argument would be needed). + */ + + current = Seek( file->af_File, position, mode ); + + if( current < 0 ) + { + AS_RecordSyncFailure( file ); + return( -1 ); + } + + file->af_BytesLeft = file->af_BufferSize; + file->af_CurrentBuf = 0; + file->af_Offset = file->af_Buffers[ 0 ]; + } + + if( file->af_SeekPastEOF ) + { + /* MH: Clear up any error flags, and restore last Res1. */ + file->af_SeekPastEOF = FALSE; + } + + SetIoErr( 0 ); + return( current ); +} diff --git a/workbench/libs/asyncio/src/SendPacket.c b/workbench/libs/asyncio/src/SendPacket.c new file mode 100644 index 000000000..cde9275c1 --- /dev/null +++ b/workbench/libs/asyncio/src/SendPacket.c @@ -0,0 +1,18 @@ +#include "async.h" + + +/* send out an async packet to the file system. */ +VOID +AS_SendPacket( struct AsyncFile *file, APTR arg2 ) +{ +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase; + + SysBase = file->af_SysBase; +#endif + + file->af_Packet.sp_Pkt.dp_Port = &file->af_PacketPort; + file->af_Packet.sp_Pkt.dp_Arg2 = ( LONG ) arg2; + PutMsg( file->af_Handler, &file->af_Packet.sp_Msg ); + file->af_PacketPending = TRUE; +} diff --git a/workbench/libs/asyncio/src/WaitPacket.c b/workbench/libs/asyncio/src/WaitPacket.c new file mode 100644 index 000000000..fd8c8b4ae --- /dev/null +++ b/workbench/libs/asyncio/src/WaitPacket.c @@ -0,0 +1,70 @@ +#include "async.h" + + +/* this function waits for a packet to come back from the file system. If no + * packet is pending, state from the previous packet is returned. This ensures + * that once an error occurs, it state is maintained for the rest of the life + * of the file handle. + * + * This function also deals with IO errors, bringing up the needed DOS + * requesters to let the user retry an operation or cancel it. + */ +LONG +AS_WaitPacket( AsyncFile *file ) +{ +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase = file->af_SysBase; + struct DosLibrary *DOSBase = file->af_DOSBase; +#endif + LONG bytes; + + if( file->af_PacketPending ) + { + while( TRUE ) + { + /* This enables signalling when a packet comes back to the port */ + file->af_PacketPort.mp_Flags = PA_SIGNAL; + + /* Wait for the packet to come back, and remove it from the message + * list. Since we know no other packets can come in to the port, we can + * safely use Remove() instead of GetMsg(). If other packets could come in, + * we would have to use GetMsg(), which correctly arbitrates access in such + * a case + */ + Remove( ( struct Node * ) WaitPort( &file->af_PacketPort ) ); + + /* set the port type back to PA_IGNORE so we won't be bothered with + * spurious signals + */ + file->af_PacketPort.mp_Flags = PA_IGNORE; + + /* mark packet as no longer pending since we removed it */ + file->af_PacketPending = FALSE; + + bytes = file->af_Packet.sp_Pkt.dp_Res1; + + if( bytes >= 0 ) + { + /* packet didn't report an error, so bye... */ + return( bytes ); + } + + /* see if the user wants to try again... */ + if( ErrorReport( file->af_Packet.sp_Pkt.dp_Res2, REPORT_STREAM, file->af_File, NULL ) ) + { + return( -1 ); + } + + /* user wants to try again, resend the packet */ + AS_SendPacket( file, + file->af_Buffers[ file->af_ReadMode ? + file->af_CurrentBuf : + 1 - file->af_CurrentBuf ] ); + } + } + + /* last packet's error code, or 0 if packet was never sent */ + SetIoErr( file->af_Packet.sp_Pkt.dp_Res2 ); + + return( file->af_Packet.sp_Pkt.dp_Res1 ); +} diff --git a/workbench/libs/asyncio/src/WriteAsync.c b/workbench/libs/asyncio/src/WriteAsync.c new file mode 100644 index 000000000..93b3d0135 --- /dev/null +++ b/workbench/libs/asyncio/src/WriteAsync.c @@ -0,0 +1,49 @@ +#include "async.h" + + +_LIBCALL LONG +WriteAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) APTR buffer, _REG( d0 ) LONG numBytes ) +{ +#ifdef ASIO_NOEXTERNALS + struct ExecBase *SysBase = file->af_SysBase; +#endif + LONG totalBytes = 0; + + /* this takes care of NIL: */ + if( !file->af_Handler ) + { + file->af_Offset = file->af_Buffers[ 0 ]; + file->af_BytesLeft = file->af_BufferSize; + return( numBytes ); + } + + while( numBytes > file->af_BytesLeft ) + { + if( file->af_BytesLeft ) + { + CopyMem( buffer, file->af_Offset, file->af_BytesLeft ); + + numBytes -= file->af_BytesLeft; + buffer = ( APTR ) ( ( ULONG ) buffer + file->af_BytesLeft ); + totalBytes += file->af_BytesLeft; + } + + if( AS_WaitPacket( file ) < 0 ) + { + return( -1 ); + } + + /* send the current buffer out to disk */ + AS_SendPacket( file, file->af_Buffers[ file->af_CurrentBuf ] ); + + file->af_CurrentBuf = 1 - file->af_CurrentBuf; + file->af_Offset = file->af_Buffers[ file->af_CurrentBuf ]; + file->af_BytesLeft = file->af_BufferSize; + } + + CopyMem( buffer, file->af_Offset, numBytes ); + file->af_BytesLeft -= numBytes; + file->af_Offset += numBytes; + + return ( totalBytes + numBytes ); +} diff --git a/workbench/libs/asyncio/src/WriteCharAsync.c b/workbench/libs/asyncio/src/WriteCharAsync.c new file mode 100644 index 000000000..abfe29197 --- /dev/null +++ b/workbench/libs/asyncio/src/WriteCharAsync.c @@ -0,0 +1,34 @@ +#include "async.h" + + +_CALL LONG +WriteCharAsync( _REG( a0 ) AsyncFile *file, _REG( d0 ) UBYTE ch ) +{ + if( file->af_BytesLeft ) + { + /* if there's any room left in the current buffer, directly write + * the byte into it, updating counters and stuff. + */ + + *file->af_Offset = ch; + --file->af_BytesLeft; + ++file->af_Offset; + + /* one byte written */ + return( 1 ); + } + + /* there was no room in the current buffer, so call the main write + * routine. This will effectively send the current buffer out to disk, + * wait for the other buffer to come back, and then put the byte into + * it. + */ + + { + TEXT c; + + c = ch; /* SAS/C workaround... */ + + return( WriteAsync( file, &c, 1 ) ); + } +} diff --git a/workbench/libs/asyncio/src/WriteLineAsync.c b/workbench/libs/asyncio/src/WriteLineAsync.c new file mode 100644 index 000000000..efe4dfba2 --- /dev/null +++ b/workbench/libs/asyncio/src/WriteLineAsync.c @@ -0,0 +1,21 @@ +#include "async.h" + + +_CALL LONG +WriteLineAsync( _REG( a0 ) AsyncFile *file, _REG( a1 ) STRPTR line ) +{ + /* Since SAS/C have an inlined strlen... */ +#if defined( NOEXTERNALS ) && !defined( __SAS ) + LONG i = 0; + STRPTR s = line; + + while( *s ) + { + ++i, ++s; + } + + return( WriteAsync( file, line, i ) ); +#else + return( WriteAsync( file, line, strlen( line ) ) ); +#endif +} diff --git a/workbench/libs/asyncio/src/async.h b/workbench/libs/asyncio/src/async.h new file mode 100644 index 000000000..dc852bcd6 --- /dev/null +++ b/workbench/libs/asyncio/src/async.h @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + + +/*****************************************************************************/ + + +#ifdef _DCC + +#ifdef ASIO_SHARED_LIB +#define _LIBCALL __geta4 _ASM _ARGS +#else +#define _LIBCALL _ASM _ARGS +#endif + +#else + +#ifdef __GNUC__ + +#define _LIBCALL + +#else /* __SASC__ */ + +#ifdef ASIO_SHARED_LIB +#define _LIBCALL __saveds _ASM _ARGS +#else +#define _LIBCALL _ASM _ARGS +#endif + +#endif /* _ GNUC_ */ + +#endif /* _DCC */ + +#define _CALL _ASM _ARGS + + +/*****************************************************************************/ + + +#ifndef ASIO_NOEXTERNALS +extern struct DosLibrary *DOSBase; +extern struct ExecBase *SysBase; +#endif + +#ifdef ASIO_SHARED_LIB +extern struct ExecBase *SysBase; +extern struct Library *UtilityBase; +extern struct DosLibrary *DOSBase; +#endif + + +/*****************************************************************************/ + + +/* this macro lets us long-align structures on the stack */ +#define D_S(type,name) char a_##name[ sizeof( type ) + 3 ]; \ + type *name = ( type * ) ( ( LONG ) ( a_##name + 3 ) & ~3 ); + +#ifndef MIN +#define MIN(a,b) ( ( a ) < ( b ) ? ( a ) : ( b ) ) +#endif + + +/*****************************************************************************/ + + +#ifdef ASIO_NOEXTERNALS +AsyncFile * +AS_OpenAsyncFH( BPTR handle, OpenModes mode, LONG bufferSize, BOOL closeIt, struct ExecBase *SysBase, struct DosLibrary *DOSBase ); +#else +AsyncFile * +AS_OpenAsyncFH( BPTR handle, OpenModes mode, LONG bufferSize, BOOL closeIt ); +#endif +VOID AS_SendPacket( AsyncFile *file, APTR arg2 ); +LONG AS_WaitPacket( AsyncFile *file ); +VOID AS_RequeuePacket( AsyncFile *file ); +VOID AS_RecordSyncFailure( AsyncFile *file ); + diff --git a/workbench/libs/asyncio/src/asyncio.conf b/workbench/libs/asyncio/src/asyncio.conf new file mode 100644 index 000000000..6191483ee --- /dev/null +++ b/workbench/libs/asyncio/src/asyncio.conf @@ -0,0 +1,26 @@ +##begin config +version 40.0 +libbase AsyncIOBase +##end config + +##begin cdef +##end cdef + +##begin cdefprivate +##end cdefprivate + +##begin functionlist +AsyncFile *OpenAsync(const STRPTR fileName, OpenModes mode, LONG bufferSize) (A0,D0,D1) +AsyncFile *OpenAsyncFromFH( BPTR handle, OpenModes mode, LONG bufferSize) (A0,D0,D1) +LONG CloseAsync(AsyncFile *file) (A0) +LONG SeekAsync(AsyncFile *file, LONG position, SeekModes mode) (A0,D0,D1) +LONG ReadAsync(AsyncFile *file, APTR buffer, LONG numBytes) (A0,A1,D0) +LONG WriteAsync( AsyncFile *file, APTR buffer, LONG numBytes) (A0,A1,D0) +LONG ReadCharAsync( AsyncFile *file) (A0) +LONG WriteCharAsync( AsyncFile *file, UBYTE ch) (A0,D0) +LONG ReadLineAsync( AsyncFile *file, APTR buffer, LONG size) (A0,A1,D0) +LONG WriteLineAsync( AsyncFile *file, STRPTR line) (A0,A1) +APTR FGetsAsync( AsyncFile *file, APTR buffer, LONG size) (A0,A1,D0) +APTR FGetsLenAsync( AsyncFile *file, APTR buffer, LONG size, LONG *length) (A0,A1,D0,A2) +LONG PeekAsync( AsyncFile *file, APTR buffer, LONG numBytes) (A0,A1,D0) +##end functionlist diff --git a/workbench/libs/asyncio/src/asynciolibsr.h b/workbench/libs/asyncio/src/asynciolibsr.h new file mode 100644 index 000000000..bc1a04624 --- /dev/null +++ b/workbench/libs/asyncio/src/asynciolibsr.h @@ -0,0 +1,4 @@ +/* Special include file needed for successfull generation of asynciolibsr.lib */ + +#define ASIO_SHARED_LIB +#include "/include/clib/asyncio_protos.h" diff --git a/workbench/libs/asyncio/src/mmakefile.src b/workbench/libs/asyncio/src/mmakefile.src new file mode 100644 index 000000000..1fc8e0a2c --- /dev/null +++ b/workbench/libs/asyncio/src/mmakefile.src @@ -0,0 +1,36 @@ + +include $(SRCDIR)/config/aros-contrib.cfg + +FUNCS := \ + CloseAsync \ + OpenAsync \ + OpenAsyncFH \ + OpenAsyncFromFH \ + PeekAsync \ + ReadAsync \ + ReadCharAsync \ + ReadLineAsync \ + RecordAsyncFailure \ + RequeuePacket \ + SeekAsync \ + SendPacket \ + WaitPacket \ + WriteAsync \ + WriteCharAsync \ + WriteLineAsync \ + FGetsLenAsync + +#MM workbench-libs-asyncio-includes : \ +#MM kernel-exec-includes \ +#MM includes-copy +#MM- workbench-libs-asyncio : linklibs + +%build_module mmake=workbench-libs-asyncio \ + modname=asyncio modtype=library \ + files="$(FUNCS)" uselibs=stdc + +#MM includes-copy +INCLUDE_FILES := $(call WILDCARD, ../include/libraries/*.h) +%copy_includes path=libraries dir=../include/libraries + +%common -- 2.11.4.GIT