| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- #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 = (HashItem *)*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 = (HashItem *)*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);
- if (smap != NULL)
- 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[256];
- 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 ...
|