michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: ** File: prlayer.c michael@0: ** Description: Routines for handling pushable protocol modules on sockets. michael@0: */ michael@0: michael@0: #include "primpl.h" michael@0: #include "prerror.h" michael@0: #include "prmem.h" michael@0: #include "prlock.h" michael@0: #include "prlog.h" michael@0: #include "prio.h" michael@0: michael@0: #include /* for memset() */ michael@0: static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack); michael@0: michael@0: void PR_CALLBACK pl_FDDestructor(PRFileDesc *fd) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: if (NULL != fd->lower) fd->lower->higher = fd->higher; michael@0: if (NULL != fd->higher) fd->higher->lower = fd->lower; michael@0: PR_DELETE(fd); michael@0: } michael@0: michael@0: /* michael@0: ** Default methods that just call down to the next fd. michael@0: */ michael@0: static PRStatus PR_CALLBACK pl_TopClose (PRFileDesc *fd) michael@0: { michael@0: PRFileDesc *top, *lower; michael@0: PRStatus rv; michael@0: michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: PR_ASSERT(fd->secret == NULL); michael@0: PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED); michael@0: michael@0: if (PR_IO_LAYER_HEAD == fd->identity) { michael@0: /* michael@0: * new style stack; close all the layers, before deleting the michael@0: * stack head michael@0: */ michael@0: rv = fd->lower->methods->close(fd->lower); michael@0: _PR_DestroyIOLayer(fd); michael@0: return rv; michael@0: } else if ((fd->higher) && (PR_IO_LAYER_HEAD == fd->higher->identity)) { michael@0: /* michael@0: * lower layers of new style stack michael@0: */ michael@0: lower = fd->lower; michael@0: /* michael@0: * pop and cleanup current layer michael@0: */ michael@0: top = PR_PopIOLayer(fd->higher, PR_TOP_IO_LAYER); michael@0: top->dtor(top); michael@0: /* michael@0: * then call lower layer michael@0: */ michael@0: return (lower->methods->close(lower)); michael@0: } else { michael@0: /* old style stack */ michael@0: top = PR_PopIOLayer(fd, PR_TOP_IO_LAYER); michael@0: top->dtor(top); michael@0: return (fd->methods->close)(fd); michael@0: } michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefRead (PRFileDesc *fd, void *buf, PRInt32 amount) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->read)(fd->lower, buf, amount); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefWrite ( michael@0: PRFileDesc *fd, const void *buf, PRInt32 amount) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->write)(fd->lower, buf, amount); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefAvailable (PRFileDesc *fd) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->available)(fd->lower); michael@0: } michael@0: michael@0: static PRInt64 PR_CALLBACK pl_DefAvailable64 (PRFileDesc *fd) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->available64)(fd->lower); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefFsync (PRFileDesc *fd) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->fsync)(fd->lower); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefSeek ( michael@0: PRFileDesc *fd, PRInt32 offset, PRSeekWhence how) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->seek)(fd->lower, offset, how); michael@0: } michael@0: michael@0: static PRInt64 PR_CALLBACK pl_DefSeek64 ( michael@0: PRFileDesc *fd, PRInt64 offset, PRSeekWhence how) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->seek64)(fd->lower, offset, how); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefFileInfo (PRFileDesc *fd, PRFileInfo *info) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->fileInfo)(fd->lower, info); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefFileInfo64 (PRFileDesc *fd, PRFileInfo64 *info) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->fileInfo64)(fd->lower, info); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefWritev (PRFileDesc *fd, const PRIOVec *iov, michael@0: PRInt32 size, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->writev)(fd->lower, iov, size, timeout); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefConnect ( michael@0: PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->connect)(fd->lower, addr, timeout); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefConnectcontinue ( michael@0: PRFileDesc *fd, PRInt16 out_flags) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->connectcontinue)(fd->lower, out_flags); michael@0: } michael@0: michael@0: static PRFileDesc* PR_CALLBACK pl_TopAccept ( michael@0: PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: PRStatus rv; michael@0: PRFileDesc *newfd, *layer = fd; michael@0: PRFileDesc *newstack; michael@0: PRBool newstyle_stack = PR_FALSE; michael@0: michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: /* test for new style stack */ michael@0: while (NULL != layer->higher) michael@0: layer = layer->higher; michael@0: newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE; michael@0: newstack = PR_NEW(PRFileDesc); michael@0: if (NULL == newstack) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: *newstack = *fd; /* make a copy of the accepting layer */ michael@0: michael@0: newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout); michael@0: if (NULL == newfd) michael@0: { michael@0: PR_DELETE(newstack); michael@0: return NULL; michael@0: } michael@0: michael@0: if (newstyle_stack) { michael@0: newstack->lower = newfd; michael@0: newfd->higher = newstack; michael@0: return newstack; michael@0: } else { michael@0: /* this PR_PushIOLayer call cannot fail */ michael@0: rv = PR_PushIOLayer(newfd, PR_TOP_IO_LAYER, newstack); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: return newfd; /* that's it */ michael@0: } michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefBind (PRFileDesc *fd, const PRNetAddr *addr) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->bind)(fd->lower, addr); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefListen (PRFileDesc *fd, PRIntn backlog) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->listen)(fd->lower, backlog); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefShutdown (PRFileDesc *fd, PRIntn how) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->shutdown)(fd->lower, how); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefRecv ( michael@0: PRFileDesc *fd, void *buf, PRInt32 amount, michael@0: PRIntn flags, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->recv)( michael@0: fd->lower, buf, amount, flags, timeout); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefSend ( michael@0: PRFileDesc *fd, const void *buf, michael@0: PRInt32 amount, PRIntn flags, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefRecvfrom ( michael@0: PRFileDesc *fd, void *buf, PRInt32 amount, michael@0: PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->recvfrom)( michael@0: fd->lower, buf, amount, flags, addr, timeout); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefSendto ( michael@0: PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, michael@0: const PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->sendto)( michael@0: fd->lower, buf, amount, flags, addr, timeout); michael@0: } michael@0: michael@0: static PRInt16 PR_CALLBACK pl_DefPoll ( michael@0: PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefAcceptread ( michael@0: PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, michael@0: PRInt32 amount, PRIntervalTime t) michael@0: { michael@0: PRInt32 nbytes; michael@0: PRStatus rv; michael@0: PRFileDesc *newstack; michael@0: PRFileDesc *layer = sd; michael@0: PRBool newstyle_stack = PR_FALSE; michael@0: michael@0: PR_ASSERT(sd != NULL); michael@0: PR_ASSERT(sd->lower != NULL); michael@0: michael@0: /* test for new style stack */ michael@0: while (NULL != layer->higher) michael@0: layer = layer->higher; michael@0: newstyle_stack = (PR_IO_LAYER_HEAD == layer->identity) ? PR_TRUE : PR_FALSE; michael@0: newstack = PR_NEW(PRFileDesc); michael@0: if (NULL == newstack) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return -1; michael@0: } michael@0: *newstack = *sd; /* make a copy of the accepting layer */ michael@0: michael@0: nbytes = sd->lower->methods->acceptread( michael@0: sd->lower, nd, raddr, buf, amount, t); michael@0: if (-1 == nbytes) michael@0: { michael@0: PR_DELETE(newstack); michael@0: return nbytes; michael@0: } michael@0: if (newstyle_stack) { michael@0: newstack->lower = *nd; michael@0: (*nd)->higher = newstack; michael@0: *nd = newstack; michael@0: return nbytes; michael@0: } else { michael@0: /* this PR_PushIOLayer call cannot fail */ michael@0: rv = PR_PushIOLayer(*nd, PR_TOP_IO_LAYER, newstack); michael@0: PR_ASSERT(PR_SUCCESS == rv); michael@0: return nbytes; michael@0: } michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefTransmitfile ( michael@0: PRFileDesc *sd, PRFileDesc *fd, const void *headers, PRInt32 hlen, michael@0: PRTransmitFileFlags flags, PRIntervalTime t) michael@0: { michael@0: PR_ASSERT(sd != NULL); michael@0: PR_ASSERT(sd->lower != NULL); michael@0: michael@0: return sd->lower->methods->transmitfile( michael@0: sd->lower, fd, headers, hlen, flags, t); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefGetsockname (PRFileDesc *fd, PRNetAddr *addr) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->getsockname)(fd->lower, addr); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefGetpeername (PRFileDesc *fd, PRNetAddr *addr) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->getpeername)(fd->lower, addr); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefGetsocketoption ( michael@0: PRFileDesc *fd, PRSocketOptionData *data) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->getsocketoption)(fd->lower, data); michael@0: } michael@0: michael@0: static PRStatus PR_CALLBACK pl_DefSetsocketoption ( michael@0: PRFileDesc *fd, const PRSocketOptionData *data) michael@0: { michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(fd->lower != NULL); michael@0: michael@0: return (fd->lower->methods->setsocketoption)(fd->lower, data); michael@0: } michael@0: michael@0: static PRInt32 PR_CALLBACK pl_DefSendfile ( michael@0: PRFileDesc *sd, PRSendFileData *sfd, michael@0: PRTransmitFileFlags flags, PRIntervalTime timeout) michael@0: { michael@0: PR_ASSERT(sd != NULL); michael@0: PR_ASSERT(sd->lower != NULL); michael@0: michael@0: return sd->lower->methods->sendfile( michael@0: sd->lower, sfd, flags, timeout); michael@0: } michael@0: michael@0: /* Methods for the top of the stack. Just call down to the next fd. */ michael@0: static PRIOMethods pl_methods = { michael@0: PR_DESC_LAYERED, michael@0: pl_TopClose, michael@0: pl_DefRead, michael@0: pl_DefWrite, michael@0: pl_DefAvailable, michael@0: pl_DefAvailable64, michael@0: pl_DefFsync, michael@0: pl_DefSeek, michael@0: pl_DefSeek64, michael@0: pl_DefFileInfo, michael@0: pl_DefFileInfo64, michael@0: pl_DefWritev, michael@0: pl_DefConnect, michael@0: pl_TopAccept, michael@0: pl_DefBind, michael@0: pl_DefListen, michael@0: pl_DefShutdown, michael@0: pl_DefRecv, michael@0: pl_DefSend, michael@0: pl_DefRecvfrom, michael@0: pl_DefSendto, michael@0: pl_DefPoll, michael@0: pl_DefAcceptread, michael@0: pl_DefTransmitfile, michael@0: pl_DefGetsockname, michael@0: pl_DefGetpeername, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: pl_DefGetsocketoption, michael@0: pl_DefSetsocketoption, michael@0: pl_DefSendfile, michael@0: pl_DefConnectcontinue, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt michael@0: }; michael@0: michael@0: PR_IMPLEMENT(const PRIOMethods*) PR_GetDefaultIOMethods(void) michael@0: { michael@0: return &pl_methods; michael@0: } /* PR_GetDefaultIOMethods */ michael@0: michael@0: PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub( michael@0: PRDescIdentity ident, const PRIOMethods *methods) michael@0: { michael@0: PRFileDesc *fd = NULL; michael@0: PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident)); michael@0: if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident)) michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: else michael@0: { michael@0: fd = PR_NEWZAP(PRFileDesc); michael@0: if (NULL == fd) michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: else michael@0: { michael@0: fd->methods = methods; michael@0: fd->dtor = pl_FDDestructor; michael@0: fd->identity = ident; michael@0: } michael@0: } michael@0: return fd; michael@0: } /* PR_CreateIOLayerStub */ michael@0: michael@0: /* michael@0: * PR_CreateIOLayer michael@0: * Create a new style stack, where the stack top is a dummy header. michael@0: * Unlike the old style stacks, the contents of the stack head michael@0: * are not modified when a layer is pushed onto or popped from a new michael@0: * style stack. michael@0: */ michael@0: michael@0: PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayer(PRFileDesc *top) michael@0: { michael@0: PRFileDesc *fd = NULL; michael@0: michael@0: fd = PR_NEWZAP(PRFileDesc); michael@0: if (NULL == fd) michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: else michael@0: { michael@0: fd->methods = &pl_methods; michael@0: fd->dtor = pl_FDDestructor; michael@0: fd->identity = PR_IO_LAYER_HEAD; michael@0: fd->higher = NULL; michael@0: fd->lower = top; michael@0: top->higher = fd; michael@0: top->lower = NULL; michael@0: } michael@0: return fd; michael@0: } /* PR_CreateIOLayer */ michael@0: michael@0: /* michael@0: * _PR_DestroyIOLayer michael@0: * Delete the stack head of a new style stack. michael@0: */ michael@0: michael@0: static PRStatus _PR_DestroyIOLayer(PRFileDesc *stack) michael@0: { michael@0: if (NULL == stack) michael@0: return PR_FAILURE; michael@0: else { michael@0: PR_DELETE(stack); michael@0: return PR_SUCCESS; michael@0: } michael@0: } /* _PR_DestroyIOLayer */ michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_PushIOLayer( michael@0: PRFileDesc *stack, PRDescIdentity id, PRFileDesc *fd) michael@0: { michael@0: PRFileDesc *insert = PR_GetIdentitiesLayer(stack, id); michael@0: michael@0: PR_ASSERT(fd != NULL); michael@0: PR_ASSERT(stack != NULL); michael@0: PR_ASSERT(insert != NULL); michael@0: PR_ASSERT(PR_IO_LAYER_HEAD != id); michael@0: if ((NULL == stack) || (NULL == fd) || (NULL == insert)) michael@0: { michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: return PR_FAILURE; michael@0: } michael@0: michael@0: if (stack == insert) michael@0: { michael@0: /* going on top of the stack */ michael@0: /* old-style stack */ michael@0: PRFileDesc copy = *stack; michael@0: *stack = *fd; michael@0: *fd = copy; michael@0: fd->higher = stack; michael@0: if (fd->lower) michael@0: { michael@0: PR_ASSERT(fd->lower->higher == stack); michael@0: fd->lower->higher = fd; michael@0: } michael@0: stack->lower = fd; michael@0: stack->higher = NULL; michael@0: } else { michael@0: /* michael@0: * going somewhere in the middle of the stack for both old and new michael@0: * style stacks, or going on top of stack for new style stack michael@0: */ michael@0: fd->lower = insert; michael@0: fd->higher = insert->higher; michael@0: michael@0: insert->higher->lower = fd; michael@0: insert->higher = fd; michael@0: } michael@0: michael@0: return PR_SUCCESS; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id) michael@0: { michael@0: PRFileDesc *extract = PR_GetIdentitiesLayer(stack, id); michael@0: michael@0: PR_ASSERT(0 != id); michael@0: PR_ASSERT(NULL != stack); michael@0: PR_ASSERT(NULL != extract); michael@0: if ((NULL == stack) || (0 == id) || (NULL == extract)) michael@0: { michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: return NULL; michael@0: } michael@0: michael@0: if (extract == stack) { michael@0: /* popping top layer of the stack */ michael@0: /* old style stack */ michael@0: PRFileDesc copy = *stack; michael@0: extract = stack->lower; michael@0: *stack = *extract; michael@0: *extract = copy; michael@0: stack->higher = NULL; michael@0: if (stack->lower) { michael@0: PR_ASSERT(stack->lower->higher == extract); michael@0: stack->lower->higher = stack; michael@0: } michael@0: } else if ((PR_IO_LAYER_HEAD == stack->identity) && michael@0: (extract == stack->lower) && (extract->lower == NULL)) { michael@0: /* michael@0: * new style stack michael@0: * popping the only layer in the stack; delete the stack too michael@0: */ michael@0: stack->lower = NULL; michael@0: _PR_DestroyIOLayer(stack); michael@0: } else { michael@0: /* for both kinds of stacks */ michael@0: extract->lower->higher = extract->higher; michael@0: extract->higher->lower = extract->lower; michael@0: } michael@0: extract->higher = extract->lower = NULL; michael@0: return extract; michael@0: } /* PR_PopIOLayer */ michael@0: michael@0: #define ID_CACHE_INCREMENT 16 michael@0: typedef struct _PRIdentity_cache michael@0: { michael@0: PRLock *ml; michael@0: char **name; michael@0: PRIntn length; michael@0: PRDescIdentity ident; michael@0: } _PRIdentity_cache; michael@0: michael@0: static _PRIdentity_cache identity_cache; michael@0: michael@0: PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name) michael@0: { michael@0: PRDescIdentity identity, length; michael@0: char **names = NULL, *name = NULL, **old = NULL; michael@0: michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident); michael@0: michael@0: if (NULL != layer_name) michael@0: { michael@0: name = (char*)PR_Malloc(strlen(layer_name) + 1); michael@0: if (NULL == name) michael@0: { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return PR_INVALID_IO_LAYER; michael@0: } michael@0: strcpy(name, layer_name); michael@0: } michael@0: michael@0: /* this initial code runs unsafe */ michael@0: retry: michael@0: PR_ASSERT(NULL == names); michael@0: /* michael@0: * In the initial round, both identity_cache.ident and michael@0: * identity_cache.length are 0, so (identity_cache.ident + 1) is greater michael@0: * than length. In later rounds, identity_cache.ident is always less michael@0: * than length, so (identity_cache.ident + 1) can be equal to but cannot michael@0: * be greater than length. michael@0: */ michael@0: length = identity_cache.length; michael@0: if ((identity_cache.ident + 1) >= length) michael@0: { michael@0: length += ID_CACHE_INCREMENT; michael@0: names = (char**)PR_CALLOC(length * sizeof(char*)); michael@0: if (NULL == names) michael@0: { michael@0: if (NULL != name) PR_DELETE(name); michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return PR_INVALID_IO_LAYER; michael@0: } michael@0: } michael@0: michael@0: /* now we get serious about thread safety */ michael@0: PR_Lock(identity_cache.ml); michael@0: PR_ASSERT(identity_cache.length == 0 || michael@0: identity_cache.ident < identity_cache.length); michael@0: identity = identity_cache.ident + 1; michael@0: if (identity >= identity_cache.length) /* there's no room */ michael@0: { michael@0: /* we have to do something - hopefully it's already done */ michael@0: if ((NULL != names) && (identity < length)) michael@0: { michael@0: /* what we did is still okay */ michael@0: memcpy( michael@0: names, identity_cache.name, michael@0: identity_cache.length * sizeof(char*)); michael@0: old = identity_cache.name; michael@0: identity_cache.name = names; michael@0: identity_cache.length = length; michael@0: names = NULL; michael@0: } michael@0: else michael@0: { michael@0: PR_Unlock(identity_cache.ml); michael@0: if (NULL != names) PR_DELETE(names); michael@0: goto retry; michael@0: } michael@0: } michael@0: if (NULL != name) /* there's a name to be stored */ michael@0: { michael@0: identity_cache.name[identity] = name; michael@0: } michael@0: identity_cache.ident = identity; michael@0: PR_ASSERT(identity_cache.ident < identity_cache.length); michael@0: PR_Unlock(identity_cache.ml); michael@0: michael@0: if (NULL != old) PR_DELETE(old); michael@0: if (NULL != names) PR_DELETE(names); michael@0: michael@0: return identity; michael@0: } /* PR_GetUniqueIdentity */ michael@0: michael@0: PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident) michael@0: { michael@0: if (!_pr_initialized) _PR_ImplicitInitialization(); michael@0: michael@0: if (PR_TOP_IO_LAYER == ident) return NULL; michael@0: michael@0: PR_ASSERT(ident <= identity_cache.ident); michael@0: return (ident > identity_cache.ident) ? NULL : identity_cache.name[ident]; michael@0: } /* PR_GetNameForIdentity */ michael@0: michael@0: PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd) michael@0: { michael@0: PR_ASSERT(NULL != fd); michael@0: if (PR_IO_LAYER_HEAD == fd->identity) { michael@0: PR_ASSERT(NULL != fd->lower); michael@0: return fd->lower->identity; michael@0: } else michael@0: return fd->identity; michael@0: } /* PR_GetLayersIdentity */ michael@0: michael@0: PR_IMPLEMENT(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id) michael@0: { michael@0: PRFileDesc *layer = fd; michael@0: michael@0: if (PR_TOP_IO_LAYER == id) { michael@0: if (PR_IO_LAYER_HEAD == fd->identity) michael@0: return fd->lower; michael@0: else michael@0: return fd; michael@0: } michael@0: michael@0: for (layer = fd; layer != NULL; layer = layer->lower) michael@0: { michael@0: if (id == layer->identity) return layer; michael@0: } michael@0: for (layer = fd; layer != NULL; layer = layer->higher) michael@0: { michael@0: if (id == layer->identity) return layer; michael@0: } michael@0: return NULL; michael@0: } /* PR_GetIdentitiesLayer */ michael@0: michael@0: void _PR_InitLayerCache(void) michael@0: { michael@0: memset(&identity_cache, 0, sizeof(identity_cache)); michael@0: identity_cache.ml = PR_NewLock(); michael@0: PR_ASSERT(NULL != identity_cache.ml); michael@0: } /* _PR_InitLayerCache */ michael@0: michael@0: void _PR_CleanupLayerCache(void) michael@0: { michael@0: if (identity_cache.ml) michael@0: { michael@0: PR_DestroyLock(identity_cache.ml); michael@0: identity_cache.ml = NULL; michael@0: } michael@0: michael@0: if (identity_cache.name) michael@0: { michael@0: PRDescIdentity ident; michael@0: michael@0: for (ident = 0; ident <= identity_cache.ident; ident++) michael@0: PR_DELETE(identity_cache.name[ident]); michael@0: michael@0: PR_DELETE(identity_cache.name); michael@0: } michael@0: } /* _PR_CleanupLayerCache */ michael@0: michael@0: /* prlayer.c */