template <typename T> // T is either uint8_t, uint16_t or uint32_t
size_t PackedIndices::unpackIndices(const uint8_t*& buffer, 
                                      T* indices, 
                                      size_t cnt) const
{
    __try
    {
        // read first index
        uint32_t index = static_cast<uint32_t>(indexPacker_.unpack(buffer));
        T* p = indices;

        (*p++) = static_cast<T>(index);

        for (size_t i = 1; i < cnt; ++i)
        {
            int64_t delta = indexPacker_.unpack(buffer);
            index = (uint32_t)(delta + index);
            (*p++) = static_cast<T>(index);
        }

        return p - indices; 
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        DebugBreak();
    }
}

//------------------------------------------------------------------------------
size_t PackedIndices::unpack(const uint8_t*& buffer, 
                             void* indices, 
                             size_t byteCount) const
{
    // get stored index count
    size_t cnt = SizePacker().unpack(buffer);
    if (cnt == 0)
        return 0;

    uint8_t width = WidthPacker().unpack(buffer);
    if (cnt * width > byteCount)
        throw std::invalid_argument("Incorrect buffer size");

    // get data width
    switch (width)
    {
    case 1: return unpackIndices(buffer, static_cast<uint8_t*>(indices), cnt);
    case 2: return unpackIndices(buffer, static_cast<uint16_t*>(indices), cnt);
    case 4: return unpackIndices(buffer, static_cast<uint32_t*>(indices), cnt);
    default: return 0;
    }
}