/*****************************************************************************/ /* MemBufLib.c Bulk data transfer from script to server using a (global section) memory buffer. Intended for transfers of multiple megabytes, tens of megabytes, and so up. Testing indicates transfers in excess of 500% of the standard mailbox bandwidth, with notable improvements to efficiency as well. YMMV with platform, O/S version and TCP/IP stack (i.e. as the relative bottlenecks shuffle about). Standard script mailbox I/O (SYS$OUTPUT, ) and memory-buffer I/O can be interleaved as required. See [SRC.HTTPD]DCLMEMBUF.C module for server implementation. This library code is a working example of the script-side implementation of the memory-buffer I/O IPC for the above module. It provide three functions: 1) MemBufLibBegin() requests the server to create a memory buffer and if/when successful maps the global section into local (script) process memory. 2) MemBufLibEnd() requests the server to flush any remaining MemBufLibWrite()n data remaining in the buffer and then delete the memory-buffer. 3) MemBufLibWrite() writes the supplied data into the memory buffer and requests the server to write that to the client. MemBufLibWrites() can be of any size. A MemBufLibWrites() with a NULL data pointer flushes any remaining data in the memory-buffer to the client. See [SRC.MISC]MEMBUFDEMO.C for example usage and build. LOGICAL NAMES ------------- None. COPYRIGHT --------- Copyright (C) 2017-2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. BUILD DETAILS ------------- See BUILD_MEMBUF.COM procedure. VERSION HISTORY --------------- 12-OCT-2017 MGD initial */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "membuflib.h" static FILE* MemBufLibCgiPlusIn; static int MemBufLibMap (char*, void**, int*); /*****************************************************************************/ /* Request the server allocate a memory buffer. |BufferSize| can be zero (server allocates its default buffer) or a positive integer of megabytes. Parameters two through four are optional and allow the calling routine to obtain a pointer to the global section name, the section memory address, and the server-reported section size. */ int MemBufLibBegin ( int BufferSize, char **GblSecNameAddr, void **GblSecPtrAddr, int *GblSecSizeAddr ) { static char GblSecName [32+1]; int gsize, status; char PlusInBuf [256]; char *cptr, *sptr, *zptr; void *gsptr; /*********/ /* begin */ /*********/ if (!(MemBufLibCgiPlusIn = fopen ("CGIPLUSIN:", "r"))) return (vaxc$errno); fputs (getenv("CGIPLUSESC"), stdout); fflush (stdout); fprintf (stdout, "BUFFER-BEGIN: %d", BufferSize); fflush (stdout); fgets (PlusInBuf, sizeof(PlusInBuf), MemBufLibCgiPlusIn); if (PlusInBuf[0] == '2') { zptr = (sptr = GblSecName) + sizeof(GblSecName)-1; for (cptr = PlusInBuf; *cptr && isdigit(*cptr); cptr++); while (*cptr && isspace(*cptr)) cptr++; while (*cptr && !isspace(*cptr) && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; status = MemBufLibMap (GblSecName, &gsptr, &gsize); } else { for (cptr = PlusInBuf; *cptr && isdigit(*cptr); cptr++); while (*cptr && isspace(*cptr)) cptr++; if (*(unsigned short*)cptr == '%X') status = strtoul (cptr+2, NULL, 16); else status = SS$_ABORT; } if (status & 1) { /* ensure the write function is reset */ MemBufLibWrite (NULL, -1); /* then set with the current global section buffer */ MemBufLibWrite (gsptr, gsize); if (GblSecNameAddr) *GblSecNameAddr = GblSecName; if (GblSecPtrAddr) *GblSecPtrAddr = gsptr; if (GblSecSizeAddr) *GblSecSizeAddr = gsize; } else { if (GblSecNameAddr) *GblSecNameAddr = NULL; if (GblSecPtrAddr) *GblSecPtrAddr = NULL; if (GblSecSizeAddr) *GblSecSizeAddr = 0; } fputs (getenv("CGIPLUSEOT"), stdout); fflush (stdout); return (status); } /*****************************************************************************/ /* Write any data remaining in the buffer and advise the server the memory buffer is no longer required. In the absence of an explicit end, the server will automaticlly delete the global common at script rundown. */ void MemBufLibEnd () { /*********/ /* begin */ /*********/ /* flush anything remaining in the write buffer */ MemBufLibWrite (NULL, 0); /* reset the write buffer */ MemBufLibWrite (NULL, -1); fputs (getenv("CGIPLUSESC"), stdout); fflush (stdout); fprintf (stdout, "!BUFFER-END:"); fflush (stdout); fputs (getenv("CGIPLUSEOT"), stdout); fflush (stdout); fclose (MemBufLibCgiPlusIn); } /*****************************************************************************/ /* */ int MemBufLibWrite (char* DataPtr, int DataCount) { static char *BufferPtr = NULL, *GblSecPtr = NULL; static int BufferCount, GblSecSize; int status, BufferAvailable; char *cptr; char PlusInBuf [256]; /*********/ /* begin */ /*********/ if (DataCount == -1) { /* if data count negative then reset the function */ BufferPtr = GblSecPtr = NULL; return (SS$_NORMAL); } if (!BufferPtr) { /* if reset then the data pointer and count represent global section */ BufferPtr = GblSecPtr = DataPtr; GblSecSize = DataCount; BufferCount = 0; return (SS$_NORMAL); } if (!GblSecPtr) return (SS$_ABORT); if (DataPtr) { /* buffer the supplied data */ while (DataCount) { if (BufferCount >= GblSecSize) { fputs (getenv("CGIPLUSESC"), stdout); fflush (stdout); fprintf (stdout, "BUFFER-WRITE: %d", BufferCount); fflush (stdout); fgets (PlusInBuf, sizeof(PlusInBuf), MemBufLibCgiPlusIn); if (PlusInBuf[0] == '2') status = SS$_NORMAL; else { for (cptr = PlusInBuf; *cptr && isdigit(*cptr); cptr++); while (*cptr && isspace(*cptr)) cptr++; if (*(unsigned short*)cptr == '%X') status = strtoul (cptr+2, NULL, 16); else status = SS$_ABORT; } fputs (getenv("CGIPLUSEOT"), stdout); fflush (stdout); if (!(status & 1)) return (status); BufferPtr = GblSecPtr; BufferCount = 0; } BufferAvailable = GblSecSize - BufferCount; if (DataCount > BufferAvailable) memcpy (BufferPtr, DataPtr, BufferAvailable); else memcpy (BufferPtr, DataPtr, BufferAvailable = DataCount); BufferPtr += BufferAvailable; DataPtr += BufferAvailable; DataCount -= BufferAvailable; BufferCount += BufferAvailable; } } else { /* flush any remaining buffer and reset */ if (BufferCount) { fputs (getenv("CGIPLUSESC"), stdout); fflush (stdout); fprintf (stdout, "BUFFER-WRITE: %d", BufferCount); fflush (stdout); fgets (PlusInBuf, sizeof(PlusInBuf), MemBufLibCgiPlusIn); if (PlusInBuf[0] == '2') status = SS$_NORMAL; else { for (cptr = PlusInBuf; *cptr && isdigit(*cptr); cptr++); while (*cptr && isspace(*cptr)) cptr++; if (*(unsigned short*)cptr == '%X') status = strtoul (cptr+2, NULL, 16); else status = SS$_ABORT; } fputs (getenv("CGIPLUSEOT"), stdout); fflush (stdout); } BufferPtr = GblSecPtr; BufferCount = 0; } return (status); } /*****************************************************************************/ /* Map the global section name. Return the global section memory address and the size of the section. MemBufLib private use only. */ static int MemBufLibMap ( char *GblSecName, void **GblSecPtrAddr, int *GblSecSizeAddr ) { /* it is recommended to map into any virtual address in the region (P0) */ static unsigned long InAddr [2] = { 0x200, 0x200 }; static int MapFlags = SEC$M_EXPREG | SEC$M_SYSGBL | SEC$M_WRT; int status, PageCount; unsigned long RetAddr [2]; char *cptr, *sptr; $DESCRIPTOR (GblSecNameDsc, ""); /*********/ /* begin */ /*********/ if (!(GblSecPtrAddr && GblSecSizeAddr)) return (SS$_BADPARAM); *GblSecPtrAddr = NULL; *GblSecSizeAddr = 0; for (cptr = GblSecName; *cptr && isspace(*cptr); cptr++); for (sptr = cptr; *sptr && !isspace(*sptr); sptr++); GblSecNameDsc.dsc$a_pointer = cptr; GblSecNameDsc.dsc$w_length = sptr - cptr; status = sys$mgblsc (&InAddr, &RetAddr, 0, MapFlags, &GblSecNameDsc, 0, 0); if (!(status & 1)) return (status); PageCount = (RetAddr[1]+1) - RetAddr[0] >> 9; *GblSecPtrAddr = (void*)RetAddr[0]; *GblSecSizeAddr = PageCount * 512; return (status); } /*****************************************************************************/