Added avifArray*() functions for basic dynamic arrays when parsing; Switched items and properties during parse to use dynamic arrays
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d76dd92..17b7560 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
 - aviffuzz tool, used in fuzzing script
 - fuzz inputs made with colorist
 - `.clang-format` file
+- `avifArray*()` functions for basic dynamic arrays when parsing
 
 ### Changed
 - Reorganized internal struct avifCodec to accomodate multiple codecs simultaneously (compile time; not exposed to API)
@@ -21,6 +22,7 @@
 - Bail out of box header advertises an impossible size
 - Ran clang-format on all of src and include
 - Fix copypasta leading to a memory leak in RGB planes
+- Switched items and properties during parse to use dynamic arrays
 
 ## [0.2.0] - 2019-06-12
 ### Added
diff --git a/include/avif/internal.h b/include/avif/internal.h
index 2eacc59..4e979f4 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -43,6 +43,20 @@
 
 void avifCalcYUVCoefficients(avifImage * image, float * outR, float * outG, float * outB);
 
+#define AVIF_ARRAY_DECLARE(TYPENAME, ITEMSTYPE, ITEMSNAME) \
+    typedef struct TYPENAME                                \
+    {                                                      \
+        ITEMSTYPE * ITEMSNAME;                             \
+        uint32_t elementSize;                              \
+        uint32_t count;                                    \
+        uint32_t capacity;                                 \
+    } TYPENAME;
+void avifArrayCreate(void * arrayStruct, uint32_t elementSize, uint32_t initialCapacity);
+uint32_t avifArrayPushIndex(void * arrayStruct);
+void * avifArrayPushPtr(void * arrayStruct);
+void avifArrayPush(void * arrayStruct, void * element);
+void avifArrayDestroy(void * arrayStruct);
+
 // ---------------------------------------------------------------------------
 // Memory management
 
diff --git a/src/read.c b/src/read.c
index 0612650..a04f56a 100644
--- a/src/read.c
+++ b/src/read.c
@@ -20,8 +20,6 @@
 
 #define AUXTYPE_SIZE 64
 #define MAX_COMPATIBLE_BRANDS 32
-#define MAX_ITEMS 8
-#define MAX_PROPERTIES 24
 
 // ---------------------------------------------------------------------------
 // Box data structures
@@ -76,6 +74,7 @@
     int thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID}
     int auxForID;       // if non-zero, this item is an auxC plane for Item #{auxForID}
 } avifItem;
+AVIF_ARRAY_DECLARE(avifItemArray, avifItem, item);
 
 // Temporary storage for ipco contents until they can be associated and memcpy'd to an avifItem
 typedef struct avifProperty
@@ -85,37 +84,49 @@
     avifAuxiliaryType auxC;
     avifColourInformationBox colr;
 } avifProperty;
+AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop);
 
 typedef struct avifData
 {
     // TODO: Everything in here using a MAX_* constant is a bit lazy; it should all be dynamic
 
     avifFileType ftyp;
-    avifItem items[MAX_ITEMS];
-    avifProperty properties[MAX_PROPERTIES];
+    avifItemArray items;
+    avifPropertyArray properties;
     int propertyCount;
 } avifData;
 
-int findItemID(avifData * data, int itemID)
+avifData * avifDataCreate()
+{
+    avifData * data = (avifData *)avifAlloc(sizeof(avifData));
+    memset(data, 0, sizeof(avifData));
+    avifArrayCreate(&data->items, sizeof(avifItem), 8);
+    avifArrayCreate(&data->properties, sizeof(avifProperty), 16);
+    return data;
+}
+
+void avifDataDestroy(avifData * data)
+{
+    avifArrayDestroy(&data->items);
+    avifArrayDestroy(&data->properties);
+    avifFree(data);
+}
+
+avifItem * avifDataFindItem(avifData * data, int itemID)
 {
     if (itemID == 0) {
-        return -1;
+        return NULL;
     }
 
-    for (int i = 0; i < MAX_ITEMS; ++i) {
-        if (data->items[i].id == itemID) {
-            return i;
+    for (uint32_t i = 0; i < data->items.count; ++i) {
+        if (data->items.item[i].id == itemID) {
+            return &data->items.item[i];
         }
     }
 
-    for (int i = 0; i < MAX_ITEMS; ++i) {
-        if (data->items[i].id == 0) {
-            data->items[i].id = itemID;
-            return i;
-        }
-    }
-
-    return -1;
+    avifItem * item = (avifItem *)avifArrayPushPtr(&data->items);
+    item->id = itemID;
+    return item;
 }
 
 // ---------------------------------------------------------------------------
@@ -170,13 +181,10 @@
             uint64_t extentLength; // unsigned int(offset_size*8) extent_length;
             CHECK(avifStreamReadUX8(&s, &extentLength, lengthSize));
 
-            int itemIndex = findItemID(data, itemID);
-            if (itemIndex == -1) {
-                return AVIF_FALSE;
-            }
-            data->items[itemIndex].id = itemID;
-            data->items[itemIndex].offset = (uint32_t)(baseOffset + extentOffset);
-            data->items[itemIndex].size = (uint32_t)extentLength;
+            avifItem * item = avifDataFindItem(data, itemID);
+            item->id = itemID;
+            item->offset = (uint32_t)(baseOffset + extentOffset);
+            item->size = (uint32_t)extentLength;
         } else {
             // TODO: support more than one extent
             return AVIF_FALSE;
@@ -190,8 +198,8 @@
     BEGIN_STREAM(s, raw, rawLen);
     CHECK(avifStreamReadAndEnforceVersion(&s, 0));
 
-    CHECK(avifStreamReadU32(&s, &data->properties[propertyIndex].ispe.width));
-    CHECK(avifStreamReadU32(&s, &data->properties[propertyIndex].ispe.height));
+    CHECK(avifStreamReadU32(&s, &data->properties.prop[propertyIndex].ispe.width));
+    CHECK(avifStreamReadU32(&s, &data->properties.prop[propertyIndex].ispe.height));
     return AVIF_TRUE;
 }
 
@@ -200,7 +208,7 @@
     BEGIN_STREAM(s, raw, rawLen);
     CHECK(avifStreamReadAndEnforceVersion(&s, 0));
 
-    CHECK(avifStreamReadString(&s, data->properties[propertyIndex].auxC.auxType, AUXTYPE_SIZE));
+    CHECK(avifStreamReadString(&s, data->properties.prop[propertyIndex].auxC.auxType, AUXTYPE_SIZE));
     return AVIF_TRUE;
 }
 
@@ -208,26 +216,26 @@
 {
     BEGIN_STREAM(s, raw, rawLen);
 
-    data->properties[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NONE;
+    data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NONE;
 
     uint8_t colourType[4]; // unsigned int(32) colour_type;
     CHECK(avifStreamRead(&s, colourType, 4));
     if (!memcmp(colourType, "rICC", 4) || !memcmp(colourType, "prof", 4)) {
-        data->properties[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_ICC;
-        data->properties[propertyIndex].colr.icc = avifStreamCurrent(&s);
-        data->properties[propertyIndex].colr.iccSize = avifStreamRemainingBytes(&s);
+        data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_ICC;
+        data->properties.prop[propertyIndex].colr.icc = avifStreamCurrent(&s);
+        data->properties.prop[propertyIndex].colr.iccSize = avifStreamRemainingBytes(&s);
     } else if (!memcmp(colourType, "nclx", 4)) {
         // unsigned int(16) colour_primaries;
-        CHECK(avifStreamReadU16(&s, &data->properties[propertyIndex].colr.nclx.colourPrimaries));
+        CHECK(avifStreamReadU16(&s, &data->properties.prop[propertyIndex].colr.nclx.colourPrimaries));
         // unsigned int(16) transfer_characteristics;
-        CHECK(avifStreamReadU16(&s, &data->properties[propertyIndex].colr.nclx.transferCharacteristics));
+        CHECK(avifStreamReadU16(&s, &data->properties.prop[propertyIndex].colr.nclx.transferCharacteristics));
         // unsigned int(16) matrix_coefficients;
-        CHECK(avifStreamReadU16(&s, &data->properties[propertyIndex].colr.nclx.matrixCoefficients));
+        CHECK(avifStreamReadU16(&s, &data->properties.prop[propertyIndex].colr.nclx.matrixCoefficients));
         // unsigned int(1) full_range_flag;
         // unsigned int(7) reserved = 0;
-        CHECK(avifStreamRead(&s, &data->properties[propertyIndex].colr.nclx.fullRangeFlag, 1));
-        data->properties[propertyIndex].colr.nclx.fullRangeFlag |= 0x80;
-        data->properties[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NCLX;
+        CHECK(avifStreamRead(&s, &data->properties.prop[propertyIndex].colr.nclx.fullRangeFlag, 1));
+        data->properties.prop[propertyIndex].colr.nclx.fullRangeFlag |= 0x80;
+        data->properties.prop[propertyIndex].colr.format = AVIF_PROFILE_FORMAT_NCLX;
     }
     return AVIF_TRUE;
 }
@@ -242,13 +250,8 @@
         avifBoxHeader header;
         CHECK(avifStreamReadBoxHeader(&s, &header));
 
-        if (data->propertyCount >= MAX_PROPERTIES) {
-            return AVIF_FALSE;
-        }
-        int propertyIndex = data->propertyCount;
-        ++data->propertyCount;
-
-        memcpy(data->properties[propertyIndex].type, header.type, 4);
+        int propertyIndex = avifArrayPushIndex(&data->properties);
+        memcpy(data->properties.prop[propertyIndex].type, header.type, 4);
         if (!memcmp(header.type, "ispe", 4)) {
             CHECK(avifParseImageSpatialExtentsProperty(data, avifStreamCurrent(&s), header.size, propertyIndex));
         }
@@ -306,26 +309,23 @@
             }
             --propertyIndex; // 1-indexed
 
-            if (propertyIndex >= MAX_PROPERTIES) {
+            if (propertyIndex >= data->properties.count) {
                 return AVIF_FALSE;
             }
 
-            int itemIndex = findItemID(data, itemID);
-            if (itemIndex == -1) {
-                return AVIF_FALSE;
-            }
+            avifItem * item = avifDataFindItem(data, itemID);
 
             // Associate property with item
-            avifProperty * prop = &data->properties[propertyIndex];
+            avifProperty * prop = &data->properties.prop[propertyIndex];
             if (!memcmp(prop->type, "ispe", 4)) {
-                data->items[itemIndex].ispePresent = AVIF_TRUE;
-                memcpy(&data->items[itemIndex].ispe, &prop->ispe, sizeof(avifImageSpatialExtents));
+                item->ispePresent = AVIF_TRUE;
+                memcpy(&item->ispe, &prop->ispe, sizeof(avifImageSpatialExtents));
             } else if (!memcmp(prop->type, "auxC", 4)) {
-                data->items[itemIndex].auxCPresent = AVIF_TRUE;
-                memcpy(&data->items[itemIndex].auxC, &prop->auxC, sizeof(avifAuxiliaryType));
+                item->auxCPresent = AVIF_TRUE;
+                memcpy(&item->auxC, &prop->auxC, sizeof(avifAuxiliaryType));
             } else if (!memcmp(prop->type, "colr", 4)) {
-                data->items[itemIndex].colrPresent = AVIF_TRUE;
-                memcpy(&data->items[itemIndex].colr, &prop->colr, sizeof(avifColourInformationBox));
+                item->colrPresent = AVIF_TRUE;
+                memcpy(&item->colr, &prop->colr, sizeof(avifColourInformationBox));
             }
         }
     }
@@ -377,12 +377,8 @@
     uint8_t itemType[4];                                // unsigned int(32) item_type;
     CHECK(avifStreamRead(&s, itemType, 4));             //
 
-    int itemIndex = findItemID(data, itemID);
-    if (itemIndex == -1) {
-        return AVIF_FALSE;
-    }
-    memcpy(data->items[itemIndex].type, itemType, sizeof(itemType));
-
+    avifItem * item = avifDataFindItem(data, itemID);
+    memcpy(item->type, itemType, sizeof(itemType));
     return AVIF_TRUE;
 }
 
@@ -461,15 +457,12 @@
 
             // Read this reference as "{fromID} is a {irefType} for {toID}"
             if (fromID && toID) {
-                int itemIndex = findItemID(data, fromID);
-                if (itemIndex == -1) {
-                    return AVIF_FALSE;
-                }
+                avifItem * item = avifDataFindItem(data, fromID);
                 if (!memcmp(irefHeader.type, "thmb", 4)) {
-                    data->items[itemIndex].thumbnailForID = toID;
+                    item->thumbnailForID = toID;
                 }
                 if (!memcmp(irefHeader.type, "auxl", 4)) {
-                    data->items[itemIndex].auxForID = toID;
+                    item->auxForID = toID;
                 }
             }
         }
@@ -560,6 +553,8 @@
 avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image, avifRawData * input)
 {
     avifCodec * codec = NULL;
+    avifData * data = NULL;
+    avifResult result = AVIF_RESULT_UNKNOWN_ERROR;
 
 #if !defined(AVIF_CODEC_AOM) && !defined(AVIF_CODEC_DAV1D)
     // Just bail out early, we're not surviving this function without a decoder compiled in
@@ -569,16 +564,16 @@
     // -----------------------------------------------------------------------
     // Parse BMFF boxes
 
-    avifData data;
-    memset(&data, 0, sizeof(data));
-    if (!avifParse(&data, input->data, input->size)) {
-        return AVIF_RESULT_BMFF_PARSE_FAILED;
+    data = avifDataCreate();
+    if (!avifParse(data, input->data, input->size)) {
+        result = AVIF_RESULT_BMFF_PARSE_FAILED;
+        goto cleanup;
     }
 
-    avifBool avifCompatible = (memcmp(data.ftyp.majorBrand, "avif", 4) == 0) ? AVIF_TRUE : AVIF_FALSE;
+    avifBool avifCompatible = (memcmp(data->ftyp.majorBrand, "avif", 4) == 0) ? AVIF_TRUE : AVIF_FALSE;
     if (!avifCompatible) {
-        for (int compatibleBrandIndex = 0; compatibleBrandIndex < data.ftyp.compatibleBrandsCount; ++compatibleBrandIndex) {
-            uint8_t * compatibleBrand = &data.ftyp.compatibleBrands[4 * compatibleBrandIndex];
+        for (int compatibleBrandIndex = 0; compatibleBrandIndex < data->ftyp.compatibleBrandsCount; ++compatibleBrandIndex) {
+            uint8_t * compatibleBrand = &data->ftyp.compatibleBrands[4 * compatibleBrandIndex];
             if (!memcmp(compatibleBrand, "avif", 4)) {
                 avifCompatible = AVIF_TRUE;
                 break;
@@ -586,7 +581,8 @@
         }
     }
     if (!avifCompatible) {
-        return AVIF_RESULT_INVALID_FTYP;
+        result = AVIF_RESULT_INVALID_FTYP;
+        goto cleanup;
     }
 
     // -----------------------------------------------------------------------
@@ -597,20 +593,22 @@
     avifItem * alphaOBUItem = NULL;
 
     // Sanity check items
-    for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) {
-        avifItem * item = &data.items[itemIndex];
+    for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
+        avifItem * item = &data->items.item[itemIndex];
         if (item->offset > input->size) {
-            return AVIF_RESULT_BMFF_PARSE_FAILED;
+            result = AVIF_RESULT_BMFF_PARSE_FAILED;
+            goto cleanup;
         }
         uint64_t offsetSize = (uint64_t)item->offset + (uint64_t)item->size;
         if (offsetSize > (uint64_t)input->size) {
-            return AVIF_RESULT_BMFF_PARSE_FAILED;
+            result = AVIF_RESULT_BMFF_PARSE_FAILED;
+            goto cleanup;
         }
     }
 
     // Find the colorOBU item
-    for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) {
-        avifItem * item = &data.items[itemIndex];
+    for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
+        avifItem * item = &data->items.item[itemIndex];
         if (!item->id || !item->size) {
             break;
         }
@@ -631,8 +629,8 @@
 
     // Find the alphaOBU item, if any
     if (colorOBUItem) {
-        for (int itemIndex = 0; itemIndex < MAX_ITEMS; ++itemIndex) {
-            avifItem * item = &data.items[itemIndex];
+        for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) {
+            avifItem * item = &data->items.item[itemIndex];
             if (!item->id || !item->size) {
                 break;
             }
@@ -655,7 +653,8 @@
     }
 
     if (colorOBU.size == 0) {
-        return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
+        result = AVIF_RESULT_NO_AV1_ITEMS_FOUND;
+        goto cleanup;
     }
     avifBool hasAlpha = (alphaOBU.size > 0) ? AVIF_TRUE : AVIF_FALSE;
 
@@ -668,8 +667,8 @@
     return AVIF_RESULT_NO_CODEC_AVAILABLE;
 #endif
     if (!codec->decode(codec, AVIF_CODEC_PLANES_COLOR, &colorOBU)) {
-        avifCodecDestroy(codec);
-        return AVIF_RESULT_DECODE_COLOR_FAILED;
+        result = AVIF_RESULT_DECODE_COLOR_FAILED;
+        goto cleanup;
     }
     avifCodecImageSize colorPlanesSize = codec->getImageSize(codec, AVIF_CODEC_PLANES_COLOR);
 
@@ -677,14 +676,14 @@
     memset(&alphaPlanesSize, 0, sizeof(alphaPlanesSize));
     if (hasAlpha) {
         if (!codec->decode(codec, AVIF_CODEC_PLANES_ALPHA, &alphaOBU)) {
-            avifCodecDestroy(codec);
-            return AVIF_RESULT_DECODE_ALPHA_FAILED;
+            result = AVIF_RESULT_DECODE_ALPHA_FAILED;
+            goto cleanup;
         }
         alphaPlanesSize = codec->getImageSize(codec, AVIF_CODEC_PLANES_ALPHA);
 
         if ((colorPlanesSize.width != alphaPlanesSize.width) || (colorPlanesSize.height != alphaPlanesSize.height)) {
-            avifCodecDestroy(codec);
-            return AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH;
+            result = AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH;
+            goto cleanup;
         }
     }
 
@@ -692,8 +691,8 @@
          ((colorOBUItem->ispe.width != colorPlanesSize.width) || (colorOBUItem->ispe.height != colorPlanesSize.height))) ||
         (alphaOBUItem && alphaOBUItem->ispePresent &&
          ((alphaOBUItem->ispe.width != alphaPlanesSize.width) || (alphaOBUItem->ispe.height != alphaPlanesSize.height)))) {
-        avifCodecDestroy(codec);
-        return AVIF_RESULT_ISPE_SIZE_MISMATCH;
+        result = AVIF_RESULT_ISPE_SIZE_MISMATCH;
+        goto cleanup;
     }
 
     if (colorOBUItem->colrPresent) {
@@ -708,8 +707,8 @@
 
     avifResult imageResult = codec->getDecodedImage(codec, image);
     if (imageResult != AVIF_RESULT_OK) {
-        avifCodecDestroy(codec);
-        return imageResult;
+        result = imageResult;
+        goto cleanup;
     }
 
 #if defined(AVIF_FIX_STUDIO_ALPHA)
@@ -733,11 +732,16 @@
     }
 #endif
 
+    decoder->ioStats.colorOBUSize = colorOBU.size;
+    decoder->ioStats.alphaOBUSize = alphaOBU.size;
+
+    result = AVIF_RESULT_OK;
+cleanup:
     if (codec) {
         avifCodecDestroy(codec);
     }
-
-    decoder->ioStats.colorOBUSize = colorOBU.size;
-    decoder->ioStats.alphaOBUSize = alphaOBU.size;
-    return AVIF_RESULT_OK;
+    if (data) {
+        avifDataDestroy(data);
+    }
+    return result;
 }
diff --git a/src/utils.c b/src/utils.c
index f05205f..aa54543 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -75,3 +75,55 @@
     return ((uint64_t)data[7] << 0) | ((uint64_t)data[6] << 8) | ((uint64_t)data[5] << 16) | ((uint64_t)data[4] << 24) |
            ((uint64_t)data[3] << 32) | ((uint64_t)data[2] << 40) | ((uint64_t)data[1] << 48) | ((uint64_t)data[0] << 56);
 }
+
+AVIF_ARRAY_DECLARE(avifArrayInternal, uint8_t, ptr);
+
+void avifArrayCreate(void * arrayStruct, uint32_t elementSize, uint32_t initialCapacity)
+{
+    avifArrayInternal * arr = (avifArrayInternal *)arrayStruct;
+    arr->elementSize = elementSize ? elementSize : 1;
+    arr->count = 0;
+    arr->capacity = initialCapacity;
+    arr->ptr = (uint8_t *)avifAlloc(arr->elementSize * arr->capacity);
+    memset(arr->ptr, 0, arr->elementSize * arr->capacity);
+}
+
+uint32_t avifArrayPushIndex(void * arrayStruct)
+{
+    avifArrayInternal * arr = (avifArrayInternal *)arrayStruct;
+    if (arr->count == arr->capacity) {
+        uint8_t * oldPtr = arr->ptr;
+        uint32_t oldByteCount = arr->elementSize * arr->capacity;
+        arr->ptr = (uint8_t *)avifAlloc(oldByteCount * 2);
+        memset(arr->ptr + oldByteCount, 0, oldByteCount);
+        memcpy(arr->ptr, oldPtr, oldByteCount);
+        arr->capacity *= 2;
+        avifFree(oldPtr);
+    }
+    ++arr->count;
+    return arr->count - 1;
+}
+
+void * avifArrayPushPtr(void * arrayStruct)
+{
+    uint32_t index = avifArrayPushIndex(arrayStruct);
+    avifArrayInternal * arr = (avifArrayInternal *)arrayStruct;
+    return &arr->ptr[index * arr->elementSize];
+}
+
+void avifArrayPush(void * arrayStruct, void * element)
+{
+    avifArrayInternal * arr = (avifArrayInternal *)arrayStruct;
+    void * newElement = avifArrayPushPtr(arr);
+    memcpy(newElement, element, arr->elementSize);
+}
+
+void avifArrayDestroy(void * arrayStruct)
+{
+    avifArrayInternal * arr = (avifArrayInternal *)arrayStruct;
+    if (arr->ptr) {
+        avifFree(arr->ptr);
+        arr->ptr = NULL;
+    }
+    memset(arr, 0, sizeof(avifArrayInternal));
+}