diff options
Diffstat (limited to 'Externals/mojoshader/1067/mojoshader_common.c')
| -rw-r--r-- | Externals/mojoshader/1067/mojoshader_common.c | 986 |
1 files changed, 986 insertions, 0 deletions
diff --git a/Externals/mojoshader/1067/mojoshader_common.c b/Externals/mojoshader/1067/mojoshader_common.c new file mode 100644 index 00000000..07505a99 --- /dev/null +++ b/Externals/mojoshader/1067/mojoshader_common.c @@ -0,0 +1,986 @@ +#define __MOJOSHADER_INTERNAL__ 1 +#include "mojoshader_internal.h" + +typedef struct HashItem +{ + const void *key; + const void *value; + struct HashItem *next; +} HashItem; + +struct HashTable +{ + HashItem **table; + uint32 table_len; + int stackable; + void *data; + HashTable_HashFn hash; + HashTable_KeyMatchFn keymatch; + HashTable_NukeFn nuke; + MOJOSHADER_malloc m; + MOJOSHADER_free f; + void *d; +}; + +static inline uint32 calc_hash(const HashTable *table, const void *key) +{ + return table->hash(key, table->data) & (table->table_len-1); +} // calc_hash + +int hash_find(const HashTable *table, const void *key, const void **_value) +{ + HashItem *i; + void *data = table->data; + const uint32 hash = calc_hash(table, key); + HashItem *prev = NULL; + for (i = table->table[hash]; i != NULL; i = i->next) + { + if (table->keymatch(key, i->key, data)) + { + if (_value != NULL) + *_value = i->value; + + // Matched! Move to the front of list for faster lookup next time. + // (stackable tables have to remain in the same order, though!) + if ((!table->stackable) && (prev != NULL)) + { + assert(prev->next == i); + prev->next = i->next; + i->next = table->table[hash]; + table->table[hash] = i; + } // if + + return 1; + } // if + + prev = i; + } // for + + return 0; +} // hash_find + +int hash_iter(const HashTable *table, const void *key, + const void **_value, void **iter) +{ + HashItem *item = *iter; + if (item == NULL) + item = table->table[calc_hash(table, key)]; + else + item = item->next; + + while (item != NULL) + { + if (table->keymatch(key, item->key, table->data)) + { + *_value = item->value; + *iter = item; + return 1; + } // if + item = item->next; + } // while + + // no more matches. + *_value = NULL; + *iter = NULL; + return 0; +} // hash_iter + +int hash_iter_keys(const HashTable *table, const void **_key, void **iter) +{ + HashItem *item = *iter; + int idx = 0; + + if (item != NULL) + { + const HashItem *orig = item; + item = item->next; + if (item == NULL) + idx = calc_hash(table, orig->key) + 1; + } // if + + while (!item && (idx < table->table_len)) + item = table->table[idx++]; // skip empty buckets... + + if (item == NULL) // no more matches? + { + *_key = NULL; + *iter = NULL; + return 0; + } // if + + *_key = item->key; + *iter = item; + return 1; +} // hash_iter_keys + +int hash_insert(HashTable *table, const void *key, const void *value) +{ + HashItem *item = NULL; + const uint32 hash = calc_hash(table, key); + if ( (!table->stackable) && (hash_find(table, key, NULL)) ) + return 0; + + // !!! FIXME: grow and rehash table if it gets too saturated. + item = (HashItem *) table->m(sizeof (HashItem), table->d); + if (item == NULL) + return -1; + + item->key = key; + item->value = value; + item->next = table->table[hash]; + table->table[hash] = item; + + return 1; +} // hash_insert + +HashTable *hash_create(void *data, const HashTable_HashFn hashfn, + const HashTable_KeyMatchFn keymatchfn, + const HashTable_NukeFn nukefn, + const int stackable, + MOJOSHADER_malloc m, MOJOSHADER_free f, void *d) +{ + const uint32 initial_table_size = 256; + const uint32 alloc_len = sizeof (HashItem *) * initial_table_size; + HashTable *table = (HashTable *) m(sizeof (HashTable), d); + if (table == NULL) + return NULL; + memset(table, '\0', sizeof (HashTable)); + + table->table = (HashItem **) m(alloc_len, d); + if (table->table == NULL) + { + f(table, d); + return NULL; + } // if + + memset(table->table, '\0', alloc_len); + table->table_len = initial_table_size; + table->stackable = stackable; + table->data = data; + table->hash = hashfn; + table->keymatch = keymatchfn; + table->nuke = nukefn; + table->m = m; + table->f = f; + table->d = d; + return table; +} // hash_create + +void hash_destroy(HashTable *table) +{ + uint32 i; + void *data = table->data; + MOJOSHADER_free f = table->f; + void *d = table->d; + for (i = 0; i < table->table_len; i++) + { + HashItem *item = table->table[i]; + while (item != NULL) + { + HashItem *next = item->next; + table->nuke(item->key, item->value, data); + f(item, d); + item = next; + } // while + } // for + + f(table->table, d); + f(table, d); +} // hash_destroy + +int hash_remove(HashTable *table, const void *key) +{ + HashItem *item = NULL; + HashItem *prev = NULL; + void *data = table->data; + const uint32 hash = calc_hash(table, key); + for (item = table->table[hash]; item != NULL; item = item->next) + { + if (table->keymatch(key, item->key, data)) + { + if (prev != NULL) + prev->next = item->next; + else + table->table[hash] = item->next; + + table->nuke(item->key, item->value, data); + table->f(item, table->d); + return 1; + } // if + + prev = item; + } // for + + return 0; +} // hash_remove + + +// this is djb's xor hashing function. +static inline uint32 hash_string_djbxor(const char *str, size_t len) +{ + register uint32 hash = 5381; + while (len--) + hash = ((hash << 5) + hash) ^ *(str++); + return hash; +} // hash_string_djbxor + +static inline uint32 hash_string(const char *str, size_t len) +{ + return hash_string_djbxor(str, len); +} // hash_string + +uint32 hash_hash_string(const void *sym, void *data) +{ + (void) data; + return hash_string((const char*) sym, strlen((const char *) sym)); +} // hash_hash_string + +int hash_keymatch_string(const void *a, const void *b, void *data) +{ + (void) data; + return (strcmp((const char *) a, (const char *) b) == 0); +} // hash_keymatch_string + + +// string -> string map... + +static void stringmap_nuke_noop(const void *key, const void *val, void *d) {} + +static void stringmap_nuke(const void *key, const void *val, void *d) +{ + StringMap *smap = (StringMap *) d; + smap->f((void *) key, smap->d); + smap->f((void *) val, smap->d); +} // stringmap_nuke + +StringMap *stringmap_create(const int copy, MOJOSHADER_malloc m, + MOJOSHADER_free f, void *d) +{ + HashTable_NukeFn nuke = copy ? stringmap_nuke : stringmap_nuke_noop; + StringMap *smap; + smap = hash_create(0,hash_hash_string,hash_keymatch_string,nuke,0,m,f,d); + smap->data = smap; + return smap; +} // stringmap_create + +void stringmap_destroy(StringMap *smap) +{ + hash_destroy(smap); +} // stringmap_destroy + +int stringmap_insert(StringMap *smap, const char *key, const char *value) +{ + assert(key != NULL); + if (smap->nuke == stringmap_nuke_noop) // no copy? + return hash_insert(smap, key, value); + + int rc = -1; + char *k = (char *) smap->m(strlen(key) + 1, smap->d); + char *v = (char *) (value ? smap->m(strlen(value) + 1, smap->d) : NULL); + int failed = ( (!k) || ((!v) && (value)) ); + + if (!failed) + { + strcpy(k, key); + if (value != NULL) + strcpy(v, value); + failed = ((rc = hash_insert(smap, k, v)) <= 0); + } // if + + if (failed) + { + smap->f(k, smap->d); + smap->f(v, smap->d); + } // if + + return rc; +} // stringmap_insert + +int stringmap_remove(StringMap *smap, const char *key) +{ + return hash_remove(smap, key); +} // stringmap_remove + +int stringmap_find(const StringMap *smap, const char *key, const char **_value) +{ + const void *value = NULL; + const int retval = hash_find(smap, key, &value); + *_value = (const char *) value; + return retval; +} // stringmap_find + + +// The string cache... !!! FIXME: use StringMap internally for this. + +typedef struct StringBucket +{ + char *string; + struct StringBucket *next; +} StringBucket; + +struct StringCache +{ + StringBucket **hashtable; + uint32 table_size; + MOJOSHADER_malloc m; + MOJOSHADER_free f; + void *d; +}; + +const char *stringcache(StringCache *cache, const char *str) +{ + return stringcache_len(cache, str, strlen(str)); +} // stringcache + +const char *stringcache_len(StringCache *cache, const char *str, + const unsigned int len) +{ + const uint8 hash = hash_string(str, len) & (cache->table_size-1); + StringBucket *bucket = cache->hashtable[hash]; + StringBucket *prev = NULL; + while (bucket) + { + const char *bstr = bucket->string; + if ((strncmp(bstr, str, len) == 0) && (bstr[len] == 0)) + { + // Matched! Move this to the front of the list. + if (prev != NULL) + { + assert(prev->next == bucket); + prev->next = bucket->next; + bucket->next = cache->hashtable[hash]; + cache->hashtable[hash] = bucket; + } // if + return bstr; // already cached + } // if + prev = bucket; + bucket = bucket->next; + } // while + + // no match, add to the table. + bucket = (StringBucket *) cache->m(sizeof (StringBucket), cache->d); + if (bucket == NULL) + return NULL; + bucket->string = (char *) cache->m(len + 1, cache->d); + if (bucket->string == NULL) + { + cache->f(bucket, cache->d); + return NULL; + } // if + memcpy(bucket->string, str, len); + bucket->string[len] = '\0'; + bucket->next = cache->hashtable[hash]; + cache->hashtable[hash] = bucket; + return bucket->string; +} // stringcache_len + +const char *stringcache_fmt(StringCache *cache, const char *fmt, ...) +{ + char buf[128]; // use the stack if reasonable. + char *ptr = NULL; + int len = 0; // number of chars, NOT counting null-terminator! + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof (buf), fmt, ap); + va_end(ap); + + if (len > sizeof (buf)) + { + ptr = (char *) cache->m(len, cache->d); + if (ptr == NULL) + return NULL; + + va_start(ap, fmt); + vsnprintf(ptr, len, fmt, ap); + va_end(ap); + } // if + + const char *retval = stringcache_len(cache, ptr ? ptr : buf, len); + if (ptr != NULL) + cache->f(ptr, cache->d); + + return retval; +} // stringcache_fmt + +StringCache *stringcache_create(MOJOSHADER_malloc m, MOJOSHADER_free f, void *d) +{ + const uint32 initial_table_size = 256; + const size_t tablelen = sizeof (StringBucket *) * initial_table_size; + StringCache *cache = (StringCache *) m(sizeof (StringCache), d); + if (!cache) + return NULL; + memset(cache, '\0', sizeof (StringCache)); + + cache->hashtable = (StringBucket **) m(tablelen, d); + if (!cache->hashtable) + { + f(cache, d); + return NULL; + } // if + memset(cache->hashtable, '\0', tablelen); + + cache->table_size = initial_table_size; + cache->m = m; + cache->f = f; + cache->d = d; + return cache; +} // stringcache_create + +void stringcache_destroy(StringCache *cache) +{ + if (cache == NULL) + return; + + MOJOSHADER_free f = cache->f; + void *d = cache->d; + size_t i; + + for (i = 0; i < cache->table_size; i++) + { + StringBucket *bucket = cache->hashtable[i]; + cache->hashtable[i] = NULL; + while (bucket) + { + StringBucket *next = bucket->next; + f(bucket->string, d); + f(bucket, d); + bucket = next; + } // while + } // for + + f(cache->hashtable, d); + f(cache, d); +} // stringcache_destroy + + +// We chain errors as a linked list with a head/tail for easy appending. +// These get flattened before passing to the application. +typedef struct ErrorItem +{ + MOJOSHADER_error error; + struct ErrorItem *next; +} ErrorItem; + +struct ErrorList +{ + ErrorItem head; + ErrorItem *tail; + int count; + MOJOSHADER_malloc m; + MOJOSHADER_free f; + void *d; +}; + +ErrorList *errorlist_create(MOJOSHADER_malloc m, MOJOSHADER_free f, void *d) +{ + ErrorList *retval = (ErrorList *) m(sizeof (ErrorList), d); + if (retval != NULL) + { + memset(retval, '\0', sizeof (ErrorList)); + retval->tail = &retval->head; + retval->m = m; + retval->f = f; + retval->d = d; + } // if + + return retval; +} // errorlist_create + + +int errorlist_add(ErrorList *list, const char *fname, + const int errpos, const char *str) +{ + return errorlist_add_fmt(list, fname, errpos, "%s", str); +} // errorlist_add + + +int errorlist_add_fmt(ErrorList *list, const char *fname, + const int errpos, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + const int retval = errorlist_add_va(list, fname, errpos, fmt, ap); + va_end(ap); + return retval; +} // errorlist_add_fmt + + +int errorlist_add_va(ErrorList *list, const char *_fname, + const int errpos, const char *fmt, va_list va) +{ + ErrorItem *error = (ErrorItem *) list->m(sizeof (ErrorItem), list->d); + if (error == NULL) + return 0; + + char *fname = NULL; + if (_fname != NULL) + { + fname = (char *) list->m(strlen(_fname) + 1, list->d); + if (fname == NULL) + { + list->f(error, list->d); + return 0; + } // if + strcpy(fname, _fname); + } // if + + char scratch[128]; + va_list ap; + va_copy(ap, va); + const int len = vsnprintf(scratch, sizeof (scratch), fmt, ap); + va_end(ap); + + char *failstr = (char *) list->m(len + 1, list->d); + if (failstr == NULL) + { + list->f(error, list->d); + list->f(fname, list->d); + return 0; + } // if + + // If we overflowed our scratch buffer, that's okay. We were going to + // allocate anyhow...the scratch buffer just lets us avoid a second + // run of vsnprintf(). + if (len < sizeof (scratch)) + strcpy(failstr, scratch); // copy it over. + else + { + va_copy(ap, va); + vsnprintf(failstr, len + 1, fmt, ap); // rebuild it. + va_end(ap); + } // else + + error->error.error = failstr; + error->error.filename = fname; + error->error.error_position = errpos; + error->next = NULL; + + list->tail->next = error; + list->tail = error; + + list->count++; + return 1; +} // errorlist_add_va + + +int errorlist_count(ErrorList *list) +{ + return list->count; +} // errorlist_count + + +MOJOSHADER_error *errorlist_flatten(ErrorList *list) +{ + if (list->count == 0) + return NULL; + + int total = 0; + MOJOSHADER_error *retval = (MOJOSHADER_error *) + list->m(sizeof (MOJOSHADER_error) * list->count, list->d); + if (retval == NULL) + return NULL; + + ErrorItem *item = list->head.next; + while (item != NULL) + { + ErrorItem *next = item->next; + // reuse the string allocations + memcpy(&retval[total], &item->error, sizeof (MOJOSHADER_error)); + list->f(item, list->d); + item = next; + total++; + } // while + + assert(total == list->count); + list->count = 0; + list->head.next = NULL; + list->tail = &list->head; + return retval; +} // errorlist_flatten + + +void errorlist_destroy(ErrorList *list) +{ + if (list == NULL) + return; + + MOJOSHADER_free f = list->f; + void *d = list->d; + ErrorItem *item = list->head.next; + while (item != NULL) + { + ErrorItem *next = item->next; + f((void *) item->error.error, d); + f((void *) item->error.filename, d); + f(item, d); + item = next; + } // while + f(list, d); +} // errorlist_destroy + + +typedef struct BufferBlock +{ + uint8 *data; + size_t bytes; + struct BufferBlock *next; +} BufferBlock; + +struct Buffer +{ + size_t total_bytes; + BufferBlock *head; + BufferBlock *tail; + size_t block_size; + MOJOSHADER_malloc m; + MOJOSHADER_free f; + void *d; +}; + +Buffer *buffer_create(size_t blksz, MOJOSHADER_malloc m, + MOJOSHADER_free f, void *d) +{ + Buffer *buffer = (Buffer *) m(sizeof (Buffer), d); + if (buffer != NULL) + { + memset(buffer, '\0', sizeof (Buffer)); + buffer->block_size = blksz; + buffer->m = m; + buffer->f = f; + buffer->d = d; + } // if + return buffer; +} // buffer_create + +char *buffer_reserve(Buffer *buffer, const size_t len) +{ + // note that we make the blocks bigger than blocksize when we have enough + // data to overfill a fresh block, to reduce allocations. + const size_t blocksize = buffer->block_size; + + if (len == 0) + return NULL; + + if (buffer->tail != NULL) + { + const size_t tailbytes = buffer->tail->bytes; + const size_t avail = (tailbytes >= blocksize) ? 0 : blocksize - tailbytes; + if (len <= avail) + { + buffer->tail->bytes += len; + buffer->total_bytes += len; + assert(buffer->tail->bytes <= blocksize); + return (char *) buffer->tail->data + tailbytes; + } // if + } // if + + // need to allocate a new block (even if a previous block wasn't filled, + // so this buffer is contiguous). + const size_t bytecount = len > blocksize ? len : blocksize; + const size_t malloc_len = sizeof (BufferBlock) + bytecount; + BufferBlock *item = (BufferBlock *) buffer->m(malloc_len, buffer->d); + if (item == NULL) + return NULL; + + item->data = ((uint8 *) item) + sizeof (BufferBlock); + item->bytes = len; + item->next = NULL; + if (buffer->tail != NULL) + buffer->tail->next = item; + else + buffer->head = item; + buffer->tail = item; + + buffer->total_bytes += len; + + return (char *) item->data; +} // buffer_reserve + +int buffer_append(Buffer *buffer, const void *_data, size_t len) +{ + const uint8 *data = (const uint8 *) _data; + + // note that we make the blocks bigger than blocksize when we have enough + // data to overfill a fresh block, to reduce allocations. + const size_t blocksize = buffer->block_size; + + if (len == 0) + return 1; + + if (buffer->tail != NULL) + { + const size_t tailbytes = buffer->tail->bytes; + const size_t avail = (tailbytes >= blocksize) ? 0 : blocksize - tailbytes; + const size_t cpy = (avail > len) ? len : avail; + if (cpy > 0) + { + memcpy(buffer->tail->data + tailbytes, data, cpy); + len -= cpy; + data += cpy; + buffer->tail->bytes += cpy; + buffer->total_bytes += cpy; + assert(buffer->tail->bytes <= blocksize); + } // if + } // if + + if (len > 0) + { + assert((!buffer->tail) || (buffer->tail->bytes == blocksize)); + const size_t bytecount = len > blocksize ? len : blocksize; + const size_t malloc_len = sizeof (BufferBlock) + bytecount; + BufferBlock *item = (BufferBlock *) buffer->m(malloc_len, buffer->d); + if (item == NULL) + return 0; + + item->data = ((uint8 *) item) + sizeof (BufferBlock); + item->bytes = len; + item->next = NULL; + if (buffer->tail != NULL) + buffer->tail->next = item; + else + buffer->head = item; + buffer->tail = item; + + memcpy(item->data, data, len); + buffer->total_bytes += len; + } // if + + return 1; +} // buffer_append + +int buffer_append_fmt(Buffer *buffer, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + const int retval = buffer_append_va(buffer, fmt, ap); + va_end(ap); + return retval; +} // buffer_append_fmt + +int buffer_append_va(Buffer *buffer, const char *fmt, va_list va) +{ + char scratch[128]; + + va_list ap; + va_copy(ap, va); + const int len = vsnprintf(scratch, sizeof (scratch), fmt, ap); + va_end(ap); + + // If we overflowed our scratch buffer, heap allocate and try again. + + if (len == 0) + return 1; // nothing to do. + else if (len < sizeof (scratch)) + return buffer_append(buffer, scratch, len); + + char *buf = (char *) buffer->m(len + 1, buffer->d); + if (buf == NULL) + return 0; + va_copy(ap, va); + vsnprintf(buf, len + 1, fmt, ap); // rebuild it. + va_end(ap); + const int retval = buffer_append(buffer, scratch, len); + buffer->f(buf, buffer->d); + return retval; +} // buffer_append_va + +size_t buffer_size(Buffer *buffer) +{ + return buffer->total_bytes; +} // buffer_size + +void buffer_empty(Buffer *buffer) +{ + BufferBlock *item = buffer->head; + while (item != NULL) + { + BufferBlock *next = item->next; + buffer->f(item, buffer->d); + item = next; + } // while + buffer->head = buffer->tail = NULL; + buffer->total_bytes = 0; +} // buffer_empty + +char *buffer_flatten(Buffer *buffer) +{ + char *retval = (char *) buffer->m(buffer->total_bytes + 1, buffer->d); + if (retval == NULL) + return NULL; + BufferBlock *item = buffer->head; + char *ptr = retval; + while (item != NULL) + { + BufferBlock *next = item->next; + memcpy(ptr, item->data, item->bytes); + ptr += item->bytes; + buffer->f(item, buffer->d); + item = next; + } // while + *ptr = '\0'; + + assert(ptr == (retval + buffer->total_bytes)); + + buffer->head = buffer->tail = NULL; + buffer->total_bytes = 0; + + return retval; +} // buffer_flatten + +char *buffer_merge(Buffer **buffers, const size_t n, size_t *_len) +{ + Buffer *first = NULL; + size_t len = 0; + size_t i; + for (i = 0; i < n; i++) + { + Buffer *buffer = buffers[i]; + if (buffer == NULL) + continue; + if (first == NULL) + first = buffer; + len += buffer->total_bytes; + } // for + + char *retval = (char *) (first ? first->m(len + 1, first->d) : NULL); + if (retval == NULL) + { + *_len = 0; + return NULL; + } // if + + *_len = len; + char *ptr = retval; + for (i = 0; i < n; i++) + { + Buffer *buffer = buffers[i]; + if (buffer == NULL) + continue; + BufferBlock *item = buffer->head; + while (item != NULL) + { + BufferBlock *next = item->next; + memcpy(ptr, item->data, item->bytes); + ptr += item->bytes; + buffer->f(item, buffer->d); + item = next; + } // while + + buffer->head = buffer->tail = NULL; + buffer->total_bytes = 0; + } // for + *ptr = '\0'; + + assert(ptr == (retval + len)); + + return retval; +} // buffer_merge + +void buffer_destroy(Buffer *buffer) +{ + if (buffer != NULL) + { + MOJOSHADER_free f = buffer->f; + void *d = buffer->d; + buffer_empty(buffer); + f(buffer, d); + } // if +} // buffer_destroy + +static int blockscmp(BufferBlock *item, const uint8 *data, size_t len) +{ + if (len == 0) + return 1; // "match" + + while (item != NULL) + { + const size_t itemremain = item->bytes; + const size_t avail = len < itemremain ? len : itemremain; + if (memcmp(item->data, data, avail) != 0) + return 0; // not a match. + + if (len == avail) + return 1; // complete match! + + len -= avail; + data += avail; + item = item->next; + } // while + + return 0; // not a complete match. +} // blockscmp + +ssize_t buffer_find(Buffer *buffer, const size_t start, + const void *_data, const size_t len) +{ + if (len == 0) + return 0; // I guess that's right. + + if (start >= buffer->total_bytes) + return -1; // definitely can't match. + + if (len > (buffer->total_bytes - start)) + return -1; // definitely can't match. + + // Find the start point somewhere in the center of a buffer. + BufferBlock *item = buffer->head; + const uint8 *ptr = item->data; + size_t pos = 0; + if (start > 0) + { + while (1) + { + assert(item != NULL); + if ((pos + item->bytes) > start) // start is in this block. + { + ptr = item->data + (start - pos); + break; + } // if + + pos += item->bytes; + item = item->next; + } // while + } // if + + // okay, we're at the origin of the search. + assert(item != NULL); + assert(ptr != NULL); + + const uint8 *data = (const uint8 *) _data; + const uint8 first = *data; + while (item != NULL) + { + const size_t itemremain = item->bytes - ((size_t)(ptr-item->data)); + ptr = (uint8 *) memchr(ptr, first, itemremain); + while (ptr != NULL) + { + const size_t retval = pos + ((size_t) (ptr - item->data)); + if (len == 1) + return retval; // we're done, here it is! + + const size_t itemremain = item->bytes - ((size_t)(ptr-item->data)); + const size_t avail = len < itemremain ? len : itemremain; + if ((avail == 0) || (memcmp(ptr, data, avail) == 0)) + { + // okay, we've got a (sub)string match! Move to the next block. + // check all blocks until we get a complete match or a failure. + if (blockscmp(item->next, data+avail, len-avail)) + return (ssize_t) retval; + } // if + + // try again, further in this block. + ptr = (uint8 *) memchr(ptr + 1, first, itemremain - 1); + } // while + + pos += item->bytes; + item = item->next; + if (item != NULL) + ptr = item->data; + } // while + + return -1; // no match found. +} // buffer_find + +// end of mojoshader_common.c ... + |