/* * Copyright (c) 2003-2008 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Basic I/O abstraction routines. */ #include #include #include #include static AG_Object errorMgr; void AG_DataSourceInitSubsystem(void) { AG_ObjectInitStatic(&errorMgr, NULL); } void AG_DataSourceDestroySubsystem(void) { AG_ObjectDestroy(&errorMgr); } /* Assign an error callback routine to a data source. */ void AG_DataSourceSetErrorFn(AG_DataSource *ds, AG_EventFn fn, const char *fmt, ...) { AG_ObjectLock(&errorMgr); ds->errorFn = AG_SetEvent(&errorMgr, NULL, fn, NULL); AG_EVENT_GET_ARGS(ds->errorFn, fmt); AG_ObjectUnlock(&errorMgr); } /* Raise a data source exception. */ void AG_DataSourceError(AG_DataSource *ds, const char *fmt, ...) { va_list args; char *msg; if (fmt != NULL) { va_start(args, fmt); Vasprintf(&msg, fmt, args); va_end(args); } else { msg = Strdup(AG_GetError()); } AG_ObjectLock(&errorMgr); AG_PostEvent(NULL, &errorMgr, ds->errorFn->name, "%s", msg); AG_ObjectUnlock(&errorMgr); } /* Enable checking of debugging information. */ void AG_DataSourceSetDebug(AG_DataSource *ds, int flag) { ds->debug = flag; } /* Write type identifier for type safety checks. */ void AG_WriteTypeCode(AG_DataSource *ds, Uint32 type) { Uint32 i = (ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(type) : AG_SwapLE32(type); if (AG_Write(ds, &i, sizeof(i), 1) != 0) AG_DataSourceError(ds, NULL); } /* Write type identifier for type safety checks (offset). */ void AG_WriteTypeCodeAt(AG_DataSource *ds, Uint32 type, off_t offs) { Uint32 i = (ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(type) : AG_SwapLE32(type); if (AG_WriteAt(ds, &i, sizeof(i), 1, offs) != 0) AG_DataSourceError(ds, NULL); } /* Write type identifier for type safety checks (error-check). */ int AG_WriteTypev(AG_DataSource *ds, Uint32 type) { Uint32 i; if (!ds->debug) { return (0); } i = (ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(type) : AG_SwapLE32(type); return AG_Write(ds, &i, sizeof(i), 1); } /* Check type identifier for type safety checks (error-check). */ int AG_CheckTypev(AG_DataSource *ds, Uint32 type) { Uint32 i; if (!ds->debug) { return (0); } if (AG_Read(ds, &i, sizeof(i), 1) != 0) { AG_SetError("Reading type ID: %s", AG_GetError()); return (-1); } i = ((ds->byte_order == AG_BYTEORDER_BE) ? AG_SwapBE32(i) : AG_SwapLE32(i)); return (i == type) ? 0 : -1; } /* * File operations. */ static AG_IOStatus FileRead(AG_DataSource *ds, void *buf, size_t size, size_t nmemb, size_t *rv) { FILE *f = AG_FILE_SOURCE(ds)->file; clearerr(f); *rv = fread(buf, size, nmemb, f) * size; if (*rv < (nmemb*size)) { if (ferror(f)) { AG_SetError(_("Read error")); return (AG_IO_ERROR); } else if (feof(f)) { return (AG_IO_EOF); } } return (AG_IO_SUCCESS); } static AG_IOStatus FileReadAt(AG_DataSource *ds, void *buf, size_t size, size_t nmemb, off_t pos, size_t *rv) { FILE *f = AG_FILE_SOURCE(ds)->file; long savedPos; savedPos = ftell(f); if (fseek(f, pos, SEEK_SET) == -1) { goto fail_seek; } clearerr(f); *rv = fread(buf, size, nmemb, f) * size; if (*rv < (nmemb*size)) { if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; } if (feof(f)) { fseek(f, savedPos, SEEK_SET); return (AG_IO_EOF); } else if (ferror(f)) { AG_SetError(_("Write Error")); return (AG_IO_ERROR); } } if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; } return (AG_IO_SUCCESS); fail_seek: AG_SetError("fseek: failed"); return (AG_IO_ERROR); } static AG_IOStatus FileWrite(AG_DataSource *ds, const void *buf, size_t size, size_t nmemb, size_t *rv) { FILE *f = AG_FILE_SOURCE(ds)->file; clearerr(f); *rv = fwrite(buf, size, nmemb, f) * size; if (*rv < (nmemb*size)) { if (ferror(f)) { AG_SetError(_("Write error")); return (AG_IO_ERROR); } else { return (AG_IO_EOF); } } return (AG_IO_SUCCESS); } static AG_IOStatus FileWriteAt(AG_DataSource *ds, const void *buf, size_t size, size_t nmemb, off_t pos, size_t *rv) { FILE *f = AG_FILE_SOURCE(ds)->file; long savedPos; savedPos = ftell(f); if (fseek(f, pos, SEEK_SET) == -1) { goto fail_seek; } clearerr(f); *rv = fwrite(buf, size, nmemb, f) * size; if (*rv < (nmemb*size)) { fseek(f, savedPos, SEEK_SET); if (feof(f)) { return (AG_IO_EOF); } else { AG_SetError(_("Write Error")); return (AG_IO_ERROR); } } if (fseek(f, savedPos, SEEK_SET) == -1) { goto fail_seek; } return (AG_IO_SUCCESS); fail_seek: AG_SetError("fseek failed"); return (AG_IO_ERROR); } static off_t FileTell(AG_DataSource *ds) { return ftell(AG_FILE_SOURCE(ds)->file); } static int FileSeek(AG_DataSource *ds, off_t offs, enum ag_seek_mode mode) { FILE *f = AG_FILE_SOURCE(ds)->file; if (fseek(f, (long)offs, (mode == AG_SEEK_SET) ? SEEK_SET : (mode == AG_SEEK_CUR) ? SEEK_CUR : SEEK_END) == -1) { AG_SetError("fseek failed"); return (-1); } return (0); } /* * Memory operations. */ static AG_IOStatus CoreRead(AG_DataSource *ds, void *buf, size_t msize, size_t nmemb, size_t *rv) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); size_t size = msize*nmemb; if (cs->offs+size >= cs->size) { AG_SetError("EOF (%u+%u >= %u)", (Uint)cs->offs, (Uint)size, (Uint)cs->size); return (AG_IO_EOF); } memcpy(buf, &cs->data[cs->offs], size); *rv = size; cs->offs += size; return (AG_IO_SUCCESS); } static AG_IOStatus CoreReadAt(AG_DataSource *ds, void *buf, size_t msize, size_t nmemb, off_t pos, size_t *rv) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); size_t size = msize*nmemb; if (pos < 0) { AG_SetError("Bad position"); return (AG_IO_ERROR); } if (pos+size >= cs->size) { AG_SetError("EOF (%u+%u >= %u)", (Uint)pos, (Uint)size, (Uint)cs->size); return (AG_IO_EOF); } memcpy(buf, &cs->data[pos], size); *rv = size; return (AG_IO_SUCCESS); } static AG_IOStatus CoreWrite(AG_DataSource *ds, const void *buf, size_t msize, size_t nmemb, size_t *rv) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); size_t size = msize*nmemb; if (cs->offs+size >= cs->size) { AG_SetError("EOF"); return (AG_IO_EOF); } memcpy(&cs->data[cs->offs], buf, size); *rv = size; cs->offs += size; return (AG_IO_SUCCESS); } static AG_IOStatus CoreAutoWrite(AG_DataSource *ds, const void *buf, size_t msize, size_t nmemb, size_t *rv) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); size_t size = msize*nmemb; cs->data = Realloc(cs->data, (cs->size+size)); memcpy(&cs->data[cs->offs], buf, size); cs->size += size; cs->offs += size; *rv = size; return (AG_IO_SUCCESS); } static AG_IOStatus CoreWriteAt(AG_DataSource *ds, const void *buf, size_t msize, size_t nmemb, off_t pos, size_t *rv) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); size_t size = msize*nmemb; if (pos < 0) { AG_SetError("Bad position"); return (AG_IO_ERROR); } if (pos+size >= cs->size) { AG_SetError("EOF"); return (AG_IO_EOF); } memcpy(&cs->data[pos], buf, size); *rv = size; return (AG_IO_SUCCESS); } static AG_IOStatus CoreAutoWriteAt(AG_DataSource *ds, const void *buf, size_t msize, size_t nmemb, off_t pos, size_t *rv) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); size_t size = msize*nmemb; if (pos < 0) { AG_SetError("Bad position"); return (AG_IO_ERROR); } if (pos+size >= cs->size) { cs->data = Realloc(cs->data, (pos+size)); cs->size = pos+size; } memcpy(&cs->data[pos], buf, size); *rv = size; return (AG_IO_SUCCESS); } static off_t CoreTell(AG_DataSource *ds) { return AG_CORE_SOURCE(ds)->offs; } static int CoreSeek(AG_DataSource *ds, off_t offs, enum ag_seek_mode mode) { AG_CoreSource *cs = AG_CORE_SOURCE(ds); off_t nOffs; switch (mode) { case AG_SEEK_SET: nOffs = offs; break; case AG_SEEK_CUR: nOffs = cs->offs + offs; break; case AG_SEEK_END: default: nOffs = cs->size - offs; break; } if (nOffs < 0 || nOffs >= cs->size) { AG_SetError("Bad offset %ld", (long)nOffs); return (-1); } cs->offs = nOffs; return (0); } /* Default error handler */ static void ErrorDefault(AG_Event *event) { AG_DataSource *ds = AG_SELF(); char *errorMsg = AG_STRING(1); AG_FatalError("Data source (%p): %s", ds, errorMsg); /* free(errorMsg); */ } void AG_DataSourceInit(AG_DataSource *ds) { ds->debug = 0; ds->byte_order = AG_BYTEORDER_BE; ds->rdLast = 0; ds->wrLast = 0; ds->rdTotal = 0; ds->wrTotal = 0; ds->read = NULL; ds->read_at = NULL; ds->write = NULL; ds->write_at = NULL; ds->tell = NULL; ds->seek = NULL; ds->close = NULL; AG_MutexInitRecursive(&ds->lock); AG_DataSourceSetErrorFn(ds, ErrorDefault, "%p", ds); } void AG_CloseDataSource(AG_DataSource *ds) { ds->close(ds); } void AG_DataSourceDestroy(AG_DataSource *ds) { AG_MutexDestroy(&ds->lock); Free(ds); } AG_DataSource * AG_OpenFileHandle(FILE *f) { AG_FileSource *fs; fs = Malloc(sizeof(AG_FileSource)); AG_DataSourceInit(&fs->ds); fs->path = NULL; fs->file = f; fs->ds.read = FileRead; fs->ds.read_at = FileReadAt; fs->ds.write = FileWrite; fs->ds.write_at = FileWriteAt; fs->ds.tell = FileTell; fs->ds.seek = FileSeek; fs->ds.close = AG_CloseFile; return (&fs->ds); } AG_DataSource * AG_OpenFile(const char *path, const char *mode) { FILE *f; if ((f = fopen(path, mode)) == NULL) { AG_SetError("Unable to open %s", path); return (NULL); } return AG_OpenFileHandle(f); } AG_DataSource * AG_OpenCore(void *data, size_t size) { AG_CoreSource *cs; cs = Malloc(sizeof(AG_CoreSource)); AG_DataSourceInit(&cs->ds); cs->data = (Uint8 *)data; cs->size = size; cs->offs = 0; cs->ds.read = CoreRead; cs->ds.read_at = CoreReadAt; cs->ds.write = CoreWrite; cs->ds.write_at = CoreWriteAt; cs->ds.tell = CoreTell; cs->ds.seek = CoreSeek; cs->ds.close = AG_CloseCore; return (&cs->ds); } AG_DataSource * AG_OpenConstCore(const void *data, size_t size) { AG_ConstCoreSource *cs; cs = Malloc(sizeof(AG_ConstCoreSource)); AG_DataSourceInit(&cs->ds); cs->data = (const Uint8 *)data; cs->size = size; cs->offs = 0; cs->ds.read = CoreRead; cs->ds.read_at = CoreReadAt; cs->ds.write = NULL; cs->ds.write_at = NULL; cs->ds.tell = CoreTell; cs->ds.seek = CoreSeek; cs->ds.close = AG_CloseCore; return (&cs->ds); } AG_DataSource * AG_OpenAutoCore(void) { AG_CoreSource *cs; cs = Malloc(sizeof(AG_CoreSource)); AG_DataSourceInit(&cs->ds); cs->data = NULL; cs->size = 0; cs->offs = 0; cs->ds.read = CoreRead; cs->ds.read_at = CoreReadAt; cs->ds.write = CoreAutoWrite; cs->ds.write_at = CoreAutoWriteAt; cs->ds.tell = CoreTell; cs->ds.seek = CoreSeek; cs->ds.close = AG_CloseAutoCore; return (&cs->ds); } void AG_SetByteOrder(AG_DataSource *ds, enum ag_byte_order order) { AG_MutexLock(&ds->lock); ds->byte_order = order; AG_MutexUnlock(&ds->lock); } void AG_SetSourceDebug(AG_DataSource *ds, int enable) { AG_MutexLock(&ds->lock); ds->debug = enable; AG_MutexUnlock(&ds->lock); } void AG_CloseFile(AG_DataSource *ds) { AG_FileSource *fs = AG_FILE_SOURCE(ds); fclose(fs->file); Free(fs->path); AG_DataSourceDestroy(ds); } void AG_CloseCore(AG_DataSource *ds) { AG_DataSourceDestroy(ds); } void AG_CloseAutoCore(AG_DataSource *ds) { Free(AG_CORE_SOURCE(ds)->data); AG_DataSourceDestroy(ds); } AG_IOStatus AG_Read(AG_DataSource *ds, void *ptr, size_t size, size_t nmemb) { AG_IOStatus rv; AG_MutexLock(&ds->lock); if (ds->read == NULL) { AG_SetError(_("Illegal operation")); return (AG_IO_ERROR); } rv = ds->read(ds, ptr, size, nmemb, &ds->rdLast); ds->rdTotal += ds->rdLast; AG_MutexUnlock(&ds->lock); return (rv); } AG_IOStatus AG_ReadAt(AG_DataSource *ds, void *ptr, size_t size, size_t nmemb, off_t pos) { AG_IOStatus rv; AG_MutexLock(&ds->lock); if (ds->read_at == NULL) { AG_SetError(_("Illegal operation")); return (AG_IO_ERROR); } rv = ds->read_at(ds, ptr, size, nmemb, pos, &ds->rdLast); ds->rdTotal += ds->rdLast; AG_MutexUnlock(&ds->lock); return (rv); } AG_IOStatus AG_Write(AG_DataSource *ds, const void *ptr, size_t size, size_t nmemb) { AG_IOStatus rv; AG_MutexLock(&ds->lock); if (ds->write == NULL) { AG_SetError(_("Illegal operation")); return (AG_IO_ERROR); } rv = ds->write(ds, ptr, size, nmemb, &ds->wrLast); ds->wrTotal += ds->wrLast; AG_MutexUnlock(&ds->lock); return (rv); } AG_IOStatus AG_WriteAt(AG_DataSource *ds, const void *ptr, size_t size, size_t nmemb, off_t pos) { AG_IOStatus rv; AG_MutexLock(&ds->lock); if (ds->write_at == NULL) { AG_SetError(_("Illegal operation")); return (AG_IO_ERROR); } rv = ds->write_at(ds, ptr, size, nmemb, pos, &ds->wrLast); ds->wrTotal += ds->wrLast; AG_MutexUnlock(&ds->lock); return (rv); } off_t AG_Tell(AG_DataSource *ds) { off_t pos; AG_MutexLock(&ds->lock); pos = (ds->tell != NULL) ? ds->tell(ds) : 0; AG_MutexUnlock(&ds->lock); return (pos); } int AG_Seek(AG_DataSource *ds, off_t pos, enum ag_seek_mode mode) { int rv; if (ds->seek == NULL) { AG_SetError(_("Illegal operation")); return (-1); } AG_MutexLock(&ds->lock); rv = ds->seek(ds, pos, mode); AG_MutexUnlock(&ds->lock); return (rv); }