[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
/*****************************************************************************/
/*
                                 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, <stdout>) 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <descrip.h>

#include <secdef.h>
#include <ssdef.h>
#include <starlet.h>

#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);
}

/*****************************************************************************/