<commit_before>// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../include/fxcrt/fx_basic.h"
CFX_BasicArray::CFX_BasicArray(int unit_size)
    : m_pData(NULL)
    , m_nSize(0)
    , m_nMaxSize(0)
    , m_nGrowBy(0)
{
    if (unit_size < 0 || unit_size > (1 << 28)) {
        m_nUnitSize = 4;
    } else {
        m_nUnitSize = unit_size;
    }
}
CFX_BasicArray::~CFX_BasicArray()
{
    FX_Free(m_pData);
}
FX_BOOL CFX_BasicArray::SetSize(int nNewSize, int nGrowBy)
{
    if (nNewSize < 0 || nNewSize > (1 << 28) / m_nUnitSize) {
        if (m_pData != NULL) {
            FX_Free(m_pData);
            m_pData = NULL;
        }
        m_nSize = m_nMaxSize = 0;
        return FALSE;
    }
    if (nGrowBy >= 0) {
        m_nGrowBy = nGrowBy;
    }
    if (nNewSize == 0) {
        if (m_pData != NULL) {
            FX_Free(m_pData);
            m_pData = NULL;
        }
        m_nSize = m_nMaxSize = 0;
    } else if (m_pData == NULL) {
        m_pData = FX_Alloc(FX_BYTE, nNewSize * m_nUnitSize);
        if (!m_pData) {
            m_nSize = m_nMaxSize = 0;
            return FALSE;
        }
        m_nSize = m_nMaxSize = nNewSize;
    } else if (nNewSize <= m_nMaxSize) {
        if (nNewSize > m_nSize) {
            FXSYS_memset32(m_pData + m_nSize * m_nUnitSize, 0, (nNewSize - m_nSize) * m_nUnitSize);
        }
        m_nSize = nNewSize;
    } else {
        int nGrowBy = m_nGrowBy;
        if (nGrowBy == 0) {
            nGrowBy = m_nSize / 8;
            nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy);
        }
        int nNewMax;
        if (nNewSize < m_nMaxSize + nGrowBy) {
            nNewMax = m_nMaxSize + nGrowBy;
        } else {
            nNewMax = nNewSize;
        }
        FX_LPBYTE pNewData = FX_Realloc(FX_BYTE, m_pData, nNewMax * m_nUnitSize);
        if (pNewData == NULL) {
            return FALSE;
        }
        FXSYS_memset32(pNewData + m_nSize * m_nUnitSize, 0, (nNewMax - m_nSize) * m_nUnitSize);
        m_pData = pNewData;
        m_nSize = nNewSize;
        m_nMaxSize = nNewMax;
    }
    return TRUE;
}
FX_BOOL CFX_BasicArray::Append(const CFX_BasicArray& src)
{
    int nOldSize = m_nSize;
    if (!SetSize(m_nSize + src.m_nSize, -1)) {
        return FALSE;
    }
    FXSYS_memcpy32(m_pData + nOldSize * m_nUnitSize, src.m_pData, src.m_nSize * m_nUnitSize);
    return TRUE;
}
FX_BOOL CFX_BasicArray::Copy(const CFX_BasicArray& src)
{
    if (!SetSize(src.m_nSize, -1)) {
        return FALSE;
    }
    FXSYS_memcpy32(m_pData, src.m_pData, src.m_nSize * m_nUnitSize);
    return TRUE;
}
FX_LPBYTE CFX_BasicArray::InsertSpaceAt(int nIndex, int nCount)
{
    if (nIndex < 0 || nCount <= 0) {
        return NULL;
    }
    if (nIndex >= m_nSize) {
        if (!SetSize(nIndex + nCount, -1)) {
            return NULL;
        }
    } else {
        int nOldSize = m_nSize;
        if (!SetSize(m_nSize + nCount, -1)) {
            return NULL;
        }
        FXSYS_memmove32(m_pData + (nIndex + nCount)*m_nUnitSize, m_pData + nIndex * m_nUnitSize,
                        (nOldSize - nIndex) * m_nUnitSize);
        FXSYS_memset32(m_pData + nIndex * m_nUnitSize, 0, nCount * m_nUnitSize);
    }
    return m_pData + nIndex * m_nUnitSize;
}
FX_BOOL CFX_BasicArray::RemoveAt(int nIndex, int nCount)
{
    if (nIndex < 0 || nCount <= 0 || m_nSize < nIndex + nCount) {
        return FALSE;
    }
    int nMoveCount = m_nSize - (nIndex + nCount);
    if (nMoveCount) {
        FXSYS_memmove32(m_pData + nIndex * m_nUnitSize, m_pData + (nIndex + nCount) * m_nUnitSize, nMoveCount * m_nUnitSize);
    }
    m_nSize -= nCount;
    return TRUE;
}
FX_BOOL CFX_BasicArray::InsertAt(int nStartIndex, const CFX_BasicArray* pNewArray)
{
    if (pNewArray == NULL) {
        return FALSE;
    }
    if (pNewArray->m_nSize == 0) {
        return TRUE;
    }
    if (!InsertSpaceAt(nStartIndex, pNewArray->m_nSize)) {
        return FALSE;
    }
    FXSYS_memcpy32(m_pData + nStartIndex * m_nUnitSize, pNewArray->m_pData, pNewArray->m_nSize * m_nUnitSize);
    return TRUE;
}
const void* CFX_BasicArray::GetDataPtr(int index) const
{
    if (index < 0 || index >= m_nSize || m_pData == NULL) {
        return NULL;
    }
    return m_pData + index * m_nUnitSize;
}
CFX_BaseSegmentedArray::CFX_BaseSegmentedArray(int unit_size, int segment_units, int index_size)
    : m_UnitSize(unit_size)
    , m_SegmentSize(segment_units)
    , m_IndexSize(index_size)
    , m_IndexDepth(0)
    , m_DataSize(0)
    , m_pIndex(NULL)
{
}
void CFX_BaseSegmentedArray::SetUnitSize(int unit_size, int segment_units, int index_size)
{
    ASSERT(m_DataSize == 0);
    m_UnitSize = unit_size;
    m_SegmentSize = segment_units;
    m_IndexSize = index_size;
}
CFX_BaseSegmentedArray::~CFX_BaseSegmentedArray()
{
    RemoveAll();
}
static void _ClearIndex(int level, int size, void** pIndex)
{
    if (level == 0) {
        FX_Free(pIndex);
        return;
    }
    for (int i = 0; i < size; i++) {
        if (pIndex[i] == NULL) {
            continue;
        }
        _ClearIndex(level - 1, size, (void**)pIndex[i]);
    }
    FX_Free(pIndex);
}
void CFX_BaseSegmentedArray::RemoveAll()
{
    if (m_pIndex == NULL) {
        return;
    }
    _ClearIndex(m_IndexDepth, m_IndexSize, (void**)m_pIndex);
    m_pIndex = NULL;
    m_IndexDepth = 0;
    m_DataSize = 0;
}
void* CFX_BaseSegmentedArray::Add()
{
    if (m_DataSize % m_SegmentSize) {
        return GetAt(m_DataSize ++);
    }
    void* pSegment = FX_Alloc(FX_BYTE, m_UnitSize * m_SegmentSize);
    if (!pSegment) {
        return NULL;
    }
    if (m_pIndex == NULL) {
        m_pIndex = pSegment;
        m_DataSize ++;
        return pSegment;
    }
    if (m_IndexDepth == 0) {
        void** pIndex = (void**)FX_Alloc(void*, m_IndexSize);
        if (pIndex == NULL) {
            FX_Free(pSegment);
            return NULL;
        }
        pIndex[0] = m_pIndex;
        pIndex[1] = pSegment;
        m_pIndex = pIndex;
        m_DataSize ++;
        m_IndexDepth ++;
        return pSegment;
    }
    int seg_index = m_DataSize / m_SegmentSize;
    if (seg_index % m_IndexSize) {
        void** pIndex = GetIndex(seg_index);
        pIndex[seg_index % m_IndexSize] = pSegment;
        m_DataSize ++;
        return pSegment;
    }
    int tree_size = 1;
    int i;
    for (i = 0; i < m_IndexDepth; i ++) {
        tree_size *= m_IndexSize;
    }
    if (m_DataSize == tree_size * m_SegmentSize) {
        void** pIndex = (void**)FX_Alloc(void*, m_IndexSize);
        if (pIndex == NULL) {
            FX_Free(pSegment);
            return NULL;
        }
        pIndex[0] = m_pIndex;
        m_pIndex = pIndex;
        m_IndexDepth ++;
    } else {
        tree_size /= m_IndexSize;
    }
    void** pSpot = (void**)m_pIndex;
    for (i = 1; i < m_IndexDepth; i ++) {
        if (pSpot[seg_index / tree_size] == NULL) {
            pSpot[seg_index / tree_size] = (void*)FX_Alloc(void*, m_IndexSize);
            if (pSpot[seg_index / tree_size] == NULL) {
                break;
            }
        }
        pSpot = (void**)pSpot[seg_index / tree_size];
        seg_index = seg_index % tree_size;
        tree_size /= m_IndexSize;
    }
    if (i < m_IndexDepth) {
        FX_Free(pSegment);
        RemoveAll();
        return NULL;
    }
    pSpot[seg_index % m_IndexSize] = pSegment;
    m_DataSize ++;
    return pSegment;
}
void** CFX_BaseSegmentedArray::GetIndex(int seg_index) const
{
    ASSERT(m_IndexDepth != 0);
    if (m_IndexDepth == 1) {
        return (void**)m_pIndex;
    } else if (m_IndexDepth == 2) {
        return (void**)((void**)m_pIndex)[seg_index / m_IndexSize];
    }
    int tree_size = 1;
    int i;
    for (i = 1; i < m_IndexDepth; i ++) {
        tree_size *= m_IndexSize;
    }
    void** pSpot = (void**)m_pIndex;
    for (i = 1; i < m_IndexDepth; i ++) {
        pSpot = (void**)pSpot[seg_index / tree_size];
        seg_index = seg_index % tree_size;
        tree_size /= m_IndexSize;
    }
    return pSpot;
}
void* CFX_BaseSegmentedArray::IterateSegment(FX_LPCBYTE pSegment, int count, FX_BOOL (*callback)(void* param, void* pData), void* param) const
{
    for (int i = 0; i < count; i ++) {
        if (!callback(param, (void*)(pSegment + i * m_UnitSize))) {
            return (void*)(pSegment + i * m_UnitSize);
        }
    }
    return NULL;
}
void* CFX_BaseSegmentedArray::IterateIndex(int level, int& start, void** pIndex, FX_BOOL (*callback)(void* param, void* pData), void* param) const
{
    if (level == 0) {
        int count = m_DataSize - start;
        if (count > m_SegmentSize) {
            count = m_SegmentSize;
        }
        start += count;
        return IterateSegment((FX_LPCBYTE)pIndex, count, callback, param);
    }
    for (int i = 0; i < m_IndexSize; i ++) {
        if (pIndex[i] == NULL) {
            continue;
        }
        void* p = IterateIndex(level - 1, start, (void**)pIndex[i], callback, param);
        if (p) {
            return p;
        }
    }
    return NULL;
}
void* CFX_BaseSegmentedArray::Iterate(FX_BOOL (*callback)(void* param, void* pData), void* param) const
{
    if (m_pIndex == NULL) {
        return NULL;
    }
    int start = 0;
    return IterateIndex(m_IndexDepth, start, (void**)m_pIndex, callback, param);
}
void* CFX_BaseSegmentedArray::GetAt(int index) const
{
    if (index < 0 || index >= m_DataSize) {
        return NULL;
    }
    if (m_IndexDepth == 0) {
        return (FX_LPBYTE)m_pIndex + m_UnitSize * index;
    }
    int seg_index = index / m_SegmentSize;
    return (FX_LPBYTE)GetIndex(seg_index)[seg_index % m_IndexSize] + (index % m_SegmentSize) * m_UnitSize;
}
void CFX_BaseSegmentedArray::Delete(int index, int count)
{
    if(index < 0 || count < 1 || index + count > m_DataSize) {
        return;
    }
    int i;
    for (i = index; i < m_DataSize - count; i ++) {
        FX_BYTE* pSrc = (FX_BYTE*)GetAt(i + count);
        FX_BYTE* pDest = (FX_BYTE*)GetAt(i);
        for (int j = 0; j < m_UnitSize; j ++) {
            pDest[j] = pSrc[j];
        }
    }
    int new_segs = (m_DataSize - count + m_SegmentSize - 1) / m_SegmentSize;
    int old_segs = (m_DataSize + m_SegmentSize - 1) / m_SegmentSize;
    if (new_segs < old_segs) {
        if(m_IndexDepth) {
            for (i = new_segs; i < old_segs; i ++) {
                void** pIndex = GetIndex(i);
                FX_Free(pIndex[i % m_IndexSize]);
                pIndex[i % m_IndexSize] = NULL;
            }
        } else {
            FX_Free(m_pIndex);
            m_pIndex = NULL;
        }
    }
    m_DataSize -= count;
}
<commit_msg>Check for integer overflow in CFX_BasicArray.<commit_after>// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../include/fxcrt/fx_basic.h"
#include "../../../third_party/numerics/safe_math.h"

CFX_BasicArray::CFX_BasicArray(int unit_size)
    : m_pData(NULL)
    , m_nSize(0)
    , m_nMaxSize(0)
    , m_nGrowBy(0)
{
    if (unit_size < 0 || unit_size > (1 << 28)) {
        m_nUnitSize = 4;
    } else {
        m_nUnitSize = unit_size;
    }
}
CFX_BasicArray::~CFX_BasicArray()
{
    FX_Free(m_pData);
}
FX_BOOL CFX_BasicArray::SetSize(int nNewSize, int nGrowBy)
{
    if (nNewSize <= 0) {
        FX_Free(m_pData);
        m_pData = NULL;
        m_nSize = m_nMaxSize = 0;
        return 0 == nNewSize;
    }

    m_nGrowBy = nGrowBy >= 0 ? nGrowBy : m_nGrowBy;

    if (m_pData == NULL) {
        base::CheckedNumeric<int> totalSize = nNewSize;
        totalSize *= m_nUnitSize;
        if (!totalSize.IsValid()) {
            m_nSize = m_nMaxSize = 0;
            return FALSE;
        }
        m_pData = FX_Alloc(FX_BYTE, totalSize.ValueOrDie());
        if (!m_pData) {
            m_nSize = m_nMaxSize = 0;
            return FALSE;
        }
        m_nSize = m_nMaxSize = nNewSize;
    } else if (nNewSize <= m_nMaxSize) {
        if (nNewSize > m_nSize) {
            FXSYS_memset32(m_pData + m_nSize * m_nUnitSize, 0, (nNewSize - m_nSize) * m_nUnitSize);
        }
        m_nSize = nNewSize;
    } else {
        int nGrowBy = m_nGrowBy;
        if (nGrowBy == 0) {
            nGrowBy = m_nSize / 8;
            nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy);
        }
        int nNewMax;
        if (nNewSize < m_nMaxSize + nGrowBy) {
            nNewMax = m_nMaxSize + nGrowBy;
        } else {
            nNewMax = nNewSize;
        }

        base::CheckedNumeric<int> totalSize = nNewMax;
        totalSize *= m_nUnitSize;
        if (!totalSize.IsValid() || nNewMax < m_nSize) {
            return FALSE;
        }
        FX_LPBYTE pNewData = FX_Realloc(FX_BYTE, m_pData, totalSize.ValueOrDie());
        if (pNewData == NULL) {
            return FALSE;
        }
        FXSYS_memset32(pNewData + m_nSize * m_nUnitSize, 0, (nNewMax - m_nSize) * m_nUnitSize);
        m_pData = pNewData;
        m_nSize = nNewSize;
        m_nMaxSize = nNewMax;
    }
    return TRUE;
}
FX_BOOL CFX_BasicArray::Append(const CFX_BasicArray& src)
{
    int nOldSize = m_nSize;
    base::CheckedNumeric<int> newSize = m_nSize;
    newSize += src.m_nSize;
    if (m_nUnitSize != src.m_nUnitSize || !newSize.IsValid() || !SetSize(newSize.ValueOrDie(), -1)) {
        return FALSE;
    }

    FXSYS_memcpy32(m_pData + nOldSize * m_nUnitSize, src.m_pData, src.m_nSize * m_nUnitSize);
    return TRUE;
}
FX_BOOL CFX_BasicArray::Copy(const CFX_BasicArray& src)
{
    if (!SetSize(src.m_nSize, -1)) {
        return FALSE;
    }
    FXSYS_memcpy32(m_pData, src.m_pData, src.m_nSize * m_nUnitSize);
    return TRUE;
}
FX_LPBYTE CFX_BasicArray::InsertSpaceAt(int nIndex, int nCount)
{
    if (nIndex < 0 || nCount <= 0) {
        return NULL;
    }
    if (nIndex >= m_nSize) {
        if (!SetSize(nIndex + nCount, -1)) {
            return NULL;
        }
    } else {
        int nOldSize = m_nSize;
        if (!SetSize(m_nSize + nCount, -1)) {
            return NULL;
        }
        FXSYS_memmove32(m_pData + (nIndex + nCount)*m_nUnitSize, m_pData + nIndex * m_nUnitSize,
                        (nOldSize - nIndex) * m_nUnitSize);
        FXSYS_memset32(m_pData + nIndex * m_nUnitSize, 0, nCount * m_nUnitSize);
    }
    return m_pData + nIndex * m_nUnitSize;
}
FX_BOOL CFX_BasicArray::RemoveAt(int nIndex, int nCount)
{
    if (nIndex < 0 || nCount <= 0 || m_nSize < nIndex + nCount) {
        return FALSE;
    }
    int nMoveCount = m_nSize - (nIndex + nCount);
    if (nMoveCount) {
        FXSYS_memmove32(m_pData + nIndex * m_nUnitSize, m_pData + (nIndex + nCount) * m_nUnitSize, nMoveCount * m_nUnitSize);
    }
    m_nSize -= nCount;
    return TRUE;
}
FX_BOOL CFX_BasicArray::InsertAt(int nStartIndex, const CFX_BasicArray* pNewArray)
{
    if (pNewArray == NULL) {
        return FALSE;
    }
    if (pNewArray->m_nSize == 0) {
        return TRUE;
    }
    if (!InsertSpaceAt(nStartIndex, pNewArray->m_nSize)) {
        return FALSE;
    }
    FXSYS_memcpy32(m_pData + nStartIndex * m_nUnitSize, pNewArray->m_pData, pNewArray->m_nSize * m_nUnitSize);
    return TRUE;
}
const void* CFX_BasicArray::GetDataPtr(int index) const
{
    if (index < 0 || index >= m_nSize || m_pData == NULL) {
        return NULL;
    }
    return m_pData + index * m_nUnitSize;
}
CFX_BaseSegmentedArray::CFX_BaseSegmentedArray(int unit_size, int segment_units, int index_size)
    : m_UnitSize(unit_size)
    , m_SegmentSize(segment_units)
    , m_IndexSize(index_size)
    , m_IndexDepth(0)
    , m_DataSize(0)
    , m_pIndex(NULL)
{
}
void CFX_BaseSegmentedArray::SetUnitSize(int unit_size, int segment_units, int index_size)
{
    ASSERT(m_DataSize == 0);
    m_UnitSize = unit_size;
    m_SegmentSize = segment_units;
    m_IndexSize = index_size;
}
CFX_BaseSegmentedArray::~CFX_BaseSegmentedArray()
{
    RemoveAll();
}
static void _ClearIndex(int level, int size, void** pIndex)
{
    if (level == 0) {
        FX_Free(pIndex);
        return;
    }
    for (int i = 0; i < size; i++) {
        if (pIndex[i] == NULL) {
            continue;
        }
        _ClearIndex(level - 1, size, (void**)pIndex[i]);
    }
    FX_Free(pIndex);
}
void CFX_BaseSegmentedArray::RemoveAll()
{
    if (m_pIndex == NULL) {
        return;
    }
    _ClearIndex(m_IndexDepth, m_IndexSize, (void**)m_pIndex);
    m_pIndex = NULL;
    m_IndexDepth = 0;
    m_DataSize = 0;
}
void* CFX_BaseSegmentedArray::Add()
{
    if (m_DataSize % m_SegmentSize) {
        return GetAt(m_DataSize ++);
    }
    void* pSegment = FX_Alloc(FX_BYTE, m_UnitSize * m_SegmentSize);
    if (!pSegment) {
        return NULL;
    }
    if (m_pIndex == NULL) {
        m_pIndex = pSegment;
        m_DataSize ++;
        return pSegment;
    }
    if (m_IndexDepth == 0) {
        void** pIndex = (void**)FX_Alloc(void*, m_IndexSize);
        if (pIndex == NULL) {
            FX_Free(pSegment);
            return NULL;
        }
        pIndex[0] = m_pIndex;
        pIndex[1] = pSegment;
        m_pIndex = pIndex;
        m_DataSize ++;
        m_IndexDepth ++;
        return pSegment;
    }
    int seg_index = m_DataSize / m_SegmentSize;
    if (seg_index % m_IndexSize) {
        void** pIndex = GetIndex(seg_index);
        pIndex[seg_index % m_IndexSize] = pSegment;
        m_DataSize ++;
        return pSegment;
    }
    int tree_size = 1;
    int i;
    for (i = 0; i < m_IndexDepth; i ++) {
        tree_size *= m_IndexSize;
    }
    if (m_DataSize == tree_size * m_SegmentSize) {
        void** pIndex = (void**)FX_Alloc(void*, m_IndexSize);
        if (pIndex == NULL) {
            FX_Free(pSegment);
            return NULL;
        }
        pIndex[0] = m_pIndex;
        m_pIndex = pIndex;
        m_IndexDepth ++;
    } else {
        tree_size /= m_IndexSize;
    }
    void** pSpot = (void**)m_pIndex;
    for (i = 1; i < m_IndexDepth; i ++) {
        if (pSpot[seg_index / tree_size] == NULL) {
            pSpot[seg_index / tree_size] = (void*)FX_Alloc(void*, m_IndexSize);
            if (pSpot[seg_index / tree_size] == NULL) {
                break;
            }
        }
        pSpot = (void**)pSpot[seg_index / tree_size];
        seg_index = seg_index % tree_size;
        tree_size /= m_IndexSize;
    }
    if (i < m_IndexDepth) {
        FX_Free(pSegment);
        RemoveAll();
        return NULL;
    }
    pSpot[seg_index % m_IndexSize] = pSegment;
    m_DataSize ++;
    return pSegment;
}
void** CFX_BaseSegmentedArray::GetIndex(int seg_index) const
{
    ASSERT(m_IndexDepth != 0);
    if (m_IndexDepth == 1) {
        return (void**)m_pIndex;
    } else if (m_IndexDepth == 2) {
        return (void**)((void**)m_pIndex)[seg_index / m_IndexSize];
    }
    int tree_size = 1;
    int i;
    for (i = 1; i < m_IndexDepth; i ++) {
        tree_size *= m_IndexSize;
    }
    void** pSpot = (void**)m_pIndex;
    for (i = 1; i < m_IndexDepth; i ++) {
        pSpot = (void**)pSpot[seg_index / tree_size];
        seg_index = seg_index % tree_size;
        tree_size /= m_IndexSize;
    }
    return pSpot;
}
void* CFX_BaseSegmentedArray::IterateSegment(FX_LPCBYTE pSegment, int count, FX_BOOL (*callback)(void* param, void* pData), void* param) const
{
    for (int i = 0; i < count; i ++) {
        if (!callback(param, (void*)(pSegment + i * m_UnitSize))) {
            return (void*)(pSegment + i * m_UnitSize);
        }
    }
    return NULL;
}
void* CFX_BaseSegmentedArray::IterateIndex(int level, int& start, void** pIndex, FX_BOOL (*callback)(void* param, void* pData), void* param) const
{
    if (level == 0) {
        int count = m_DataSize - start;
        if (count > m_SegmentSize) {
            count = m_SegmentSize;
        }
        start += count;
        return IterateSegment((FX_LPCBYTE)pIndex, count, callback, param);
    }
    for (int i = 0; i < m_IndexSize; i ++) {
        if (pIndex[i] == NULL) {
            continue;
        }
        void* p = IterateIndex(level - 1, start, (void**)pIndex[i], callback, param);
        if (p) {
            return p;
        }
    }
    return NULL;
}
void* CFX_BaseSegmentedArray::Iterate(FX_BOOL (*callback)(void* param, void* pData), void* param) const
{
    if (m_pIndex == NULL) {
        return NULL;
    }
    int start = 0;
    return IterateIndex(m_IndexDepth, start, (void**)m_pIndex, callback, param);
}
void* CFX_BaseSegmentedArray::GetAt(int index) const
{
    if (index < 0 || index >= m_DataSize) {
        return NULL;
    }
    if (m_IndexDepth == 0) {
        return (FX_LPBYTE)m_pIndex + m_UnitSize * index;
    }
    int seg_index = index / m_SegmentSize;
    return (FX_LPBYTE)GetIndex(seg_index)[seg_index % m_IndexSize] + (index % m_SegmentSize) * m_UnitSize;
}
void CFX_BaseSegmentedArray::Delete(int index, int count)
{
    if(index < 0 || count < 1 || index + count > m_DataSize) {
        return;
    }
    int i;
    for (i = index; i < m_DataSize - count; i ++) {
        FX_BYTE* pSrc = (FX_BYTE*)GetAt(i + count);
        FX_BYTE* pDest = (FX_BYTE*)GetAt(i);
        for (int j = 0; j < m_UnitSize; j ++) {
            pDest[j] = pSrc[j];
        }
    }
    int new_segs = (m_DataSize - count + m_SegmentSize - 1) / m_SegmentSize;
    int old_segs = (m_DataSize + m_SegmentSize - 1) / m_SegmentSize;
    if (new_segs < old_segs) {
        if(m_IndexDepth) {
            for (i = new_segs; i < old_segs; i ++) {
                void** pIndex = GetIndex(i);
                FX_Free(pIndex[i % m_IndexSize]);
                pIndex[i % m_IndexSize] = NULL;
            }
        } else {
            FX_Free(m_pIndex);
            m_pIndex = NULL;
        }
    }
    m_DataSize -= count;
}
<|endoftext|>


<commit_before>#include "fileseq.h"
#include "pad.h"
#include "private/frameset_p.h"
#include "private/sequence_p.h"
#include "private/strings.h"

#include <algorithm>
#include <cerrno>
#include <cstring>
#include <dirent.h>
#include <iterator>
#include <map>
#include <string>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utility>

namespace fileseq {

std::string framesToFrameRange(const Frames &frames,
                               bool sorted, int zfill) {

    Frames::size_type count = frames.size();
    if (count == 0) {
        return "";
    }

    if (count == 1) {
        return internal::zfill(frames[0], zfill);
    }

    Frames::const_iterator framesIt = frames.begin();
    Frames::const_iterator framesEnd = frames.end();

    Frames sortedFrames;

    if (sorted) {
        // Copy
        sortedFrames = frames;

        // Sort
        std::sort(sortedFrames.begin(), sortedFrames.end());

        framesIt = sortedFrames.begin();
        framesEnd = sortedFrames.end();
    }

    long step = 0;
    bool hasWritten = false;
    Frames::size_type i = 0;

    std::string start, end;
    std::stringstream buf;

    while (framesIt != framesEnd) {

        // Items left
        count = std::distance(framesIt, framesEnd);

        // If we get to the last element, just write it and end
        if (count <= 2) {
            for (i = 0; i < count; ++i) {
                if (hasWritten) {
                    buf << ",";
                }
                buf << internal::zfill(framesIt[i], zfill);
            }
            hasWritten = true;
            break;
        }

        // At this point, we have 3 or more frames to check.
        // Scan the current window of the slice to see how
        // many frames we can consume into a group
        step = framesIt[1] - framesIt[0];

        // Scan and update the pointer index until we get
        // to a point where the step changes
        for (i = 0; i < (count-1); ++i) {

            if ((framesIt[i+1] - framesIt[i]) != step) {
                // We have scanned as many frames as we can
                // for this group. Now write them and stop
                // looping on this window, because the step
                // has changed.
                break;
            }
        }

        // Subsequent groups are comma-separated
        if (hasWritten) {
            buf << ",";
            hasWritten = true;
        }

        // We only have a single frame to write for this group
        if (i == 0) {
            buf << internal::zfill(framesIt[0], zfill);
            framesIt++;
            hasWritten = true;
            continue;
        }

        // First do a check to see if we could have gotten a larger range
        // out of subsequent values with a different step size
        if (i == 1 && count > 3) {
            // Check if the next two pairwise frames have the same step.
            // If so, then it is better than our current grouping.
            if ((framesIt[2] - framesIt[1]) == (framesIt[3] - framesIt[2])) {
                // Just consume the first frame, and allow the next
                // loop to scan the new stepping
                buf << internal::zfill(framesIt[0], zfill);
                framesIt++;
                hasWritten = true;
                continue;
            }
        }

        // Otherwise write out this step range
        start = internal::zfill(framesIt[0], zfill);
        end = internal::zfill(framesIt[i], zfill);
        buf << start << "-" << end;

        if (step > 1) {
            buf << "x" << step;
        }

        hasWritten = true;

        // Advance our iterator to the end of the current window
        // so that we can test the next available number
        framesIt += (i+1);
    }

    return buf.str();
}

bool isFrameRange(const std::string &frange) {
    internal::RangeMatches matches;
    return frameRangeMatches(matches, frange);
}

FileSequence findSequenceOnDisk(const std::string &pattern, Status* ok) {
    return findSequenceOnDisk(pattern, PadStyleDefault, ok);
}

FileSequence findSequenceOnDisk(const std::string &pattern, PadStyle style, Status* ok) {
    if (ok != NULL) ok->clearError();

    Status localOk;
    FileSequence fs(pattern, style, &localOk);

    if (!localOk) {
        // Treat a bad pattern as a non-match
        std::cerr << "fileseq: " << localOk << std::endl;
        return FileSequence();
    }

    FileSequences seqs;
    localOk = findSequencesOnDisk(seqs, fs.dirname());
    if (!localOk) {
        if (ok != NULL) {
            std::ostringstream err;
            err << "Failed to find " << pattern << ": " << localOk;
            ok->setError(err.str());
        }
        return FileSequence();
    }

    const std::string &base = fs.basename();
    const std::string &ext = fs.ext();

    FileSequences::iterator it;
    for (it = seqs.begin(); it != seqs.end(); ++it) {
        // Find the first match and return it
        if (it->basename() == base && it->ext() == ext) {
            it->setPaddingStyle(style);
            return (*it);
        }
    }

    // If we get this far, we didn't find a match
    return FileSequence();
}

Status findSequencesOnDisk(FileSequences &seqs,
                           const std::string &path,
                           bool hiddenFiles,
                           bool singleFiles,
                           PadStyle style) {

    // Flags
    FindSequenceOpts opts = kNoOpt;
    if (hiddenFiles) {
        opts = opts | kOptHiddenFiles;
    }
    if (singleFiles) {
        opts = opts | kOptSingleFiles;
    }

    // Delegate
    return findSequencesOnDisk(seqs, path, opts, style);
}

// Wrapper to make sure a DIR* is closed properly
// when it goes out of scope
class DirCloser {
public:
    DirCloser(DIR* d) : m_dir(d) {}

    ~DirCloser() {
        if (m_dir != NULL) {
            closedir(m_dir);
        }
    }

private:
    DIR* m_dir;
};

// Struct containing the ongoing parsing info
// about a sequence while reading a directory
struct SeqInfo {
    SeqInfo() : minWidth(0), padding(), frames() {}

    size_t minWidth;
    std::string padding;
    Frames frames;
};

// Key for sequence results map: basename, extension
typedef std::pair< const std::string, const std::string > SeqKey;
// Map type to track sequence results in a directory
typedef std::map< SeqKey, SeqInfo* > SeqsMap;

// Wrapper to make sure a SeqsMap is cleaned up properly
// when it goes out of scope
class SeqsMapCloser {
public:
    SeqsMapCloser(SeqsMap* m) : m_map(m) {}

    ~SeqsMapCloser() {
        if (m_map == NULL) {
            return;
        }

        SeqsMap::iterator it;
        for (it = m_map->begin(); it != m_map->end(); ++it) {
            if (it->second != NULL) {
                delete it->second;
                it->second = NULL;
            }
        }
    }

private:
    SeqsMap* m_map;
};

Status findSequencesOnDisk(FileSequences &seqs,
                           const std::string &path,
                           FindSequenceOpts opts,
                           PadStyle style) {

    Status status;

    using namespace internal;

    // Options
    bool hiddenFiles = opts & kOptHiddenFiles;
    bool singleFiles = opts & kOptSingleFiles;

    // Open the directory
    DIR* dir;
    if ( (dir = opendir(path.c_str())) == NULL ) {
        status.setError(std::strerror(errno));
        return status;
    }

    DirCloser dirCloser(dir); // close when scope ends

    const PaddingMapper &padder = getPadMapperForStyle(style);

    // Track members of file sequences as we scan
    SeqsMap seqsMap;
    SeqsMapCloser seqsMapCloser(&seqsMap);

    // Track individual files (if option allows)
    FileSequences files;

    // Get our root search path and make sure it ends with
    // a path sep character
    std::string root = path;
    if ( !root.empty() && (root[root.size()-1] != strings::kPathSep) ) {
        root.append( 1, strings::kPathSep );
    }

    // Prep a string buffer that we can reuse to constantly build strings
    std::ostringstream buf(root, std::ios_base::out | std::ios_base::ate);

    Frame frame;
    size_t frameWidth = 0;
    std::string name;
    FileSequence fs;
    SeqInfo* seqInfo;
    SeqPatternMatch match;
    SeqsMap::iterator seqFound;

    struct stat f_stat;
    struct dirent* d_ent;

    errno = 0;

    // Read dir and sort files into groups
    while ( (d_ent = readdir(dir)) != NULL ) {

        // Is it a directory?
        if (d_ent->d_type == DT_DIR) {
            // Skip directories
            continue;
        }

        // Get the string name of the file
        name.assign(&(d_ent->d_name[0]));

        // Is it a hidden file?
        if (hiddenFiles && name[0] == '.' ) {
            continue;
        }

        // Is it a symlink?
        if (d_ent->d_type == DT_LNK) {

            // See if we are pointing to a directory
            buf << name;
            if ( stat(buf.str().c_str(), &f_stat) == -1 ) {

                std::stringstream err;
                err << "Error reading symlink " << buf.str()
                    << ": " << std::strerror(errno);

                // TODO: Maybe don't bail on the entire loop
                // if we can't read the symlink.
                // Report the error by some means (logger, ...)
                // and continue collecting

                status.setError(err.str());
                return status;
            }

            buf.str(root); // reset

            if (S_ISDIR(f_stat.st_mode)) {
                // Also skip symlinks that point to directories
                continue;
            }

        }

        // Are we matching a single frame (no range)?
        if (!getSingleFrameMatch(match, name)) {

            // Only keep it if we wanted single files
            if (singleFiles) {
                buf << name;

                fs = FileSequence(buf.str(), style, &status);
                if (!status) {
                    return status;
                }

                fs.setFrameSet(FrameSet());
                fs.setPadding("");
                files.push_back(fs);

                buf.str(root); // reset
            }

            continue;
        }

        char* pEnd;
        frame = Frame(std::strtol(match.range.c_str(), &pEnd, 10));

        frameWidth = match.range.size();

        SeqKey key = std::make_pair(match.base, match.ext);
        seqFound = seqsMap.find(key);

        if (seqFound == seqsMap.end()) {

            // Doesn't exist yet. Create new entry
            seqInfo = new SeqInfo;
            seqInfo->minWidth = frameWidth;
            seqInfo->padding = padder.getPaddingChars(frameWidth);
            seqsMap[key] = seqInfo;

        } else {

            // Exists
            seqInfo = seqFound->second;

            // Keep trying to find the smallest padding width
            // in the sequence
            if (frameWidth < seqInfo->minWidth) {
                seqInfo->minWidth = frameWidth;
                seqInfo->padding = padder.getPaddingChars(frameWidth);
            }
        }

        seqInfo->frames.push_back(frame);

        // Got what we needed. On to the next file, if available
        errno = 0;
    }

    // Any errors reading the directory listing?
    if ( errno != 0 ) {
        status.setError( std::strerror(errno) );
        return status;
    }

    // Now that the directory listing has been read,
    // do a pass on the collected groups of files to
    // convert them into sequence results

    seqs.reserve(seqsMap.size());

    size_t pos = 0;
    char digit;
    std::string ext, pad, frange;

    SeqsMap::const_iterator seqsIt;
    for (seqsIt = seqsMap.begin(); seqsIt != seqsMap.end(); ++seqsIt) {

        seqInfo = seqsIt->second;

        name = seqsIt->first.first;
        ext = seqsIt->first.second;
        pad = seqInfo->padding;

        if (seqInfo->frames.size() == 1) {

            buf.str("");
            buf << seqInfo->frames[0];
            frange = buf.str();

            if (!name.empty()) {
                // Make sure a non-sequence file doesn't accidentally
                // get reparsed as a range.
                pos = 1;
                // Check if the parsed number was preceded by a "-",
                // if so, check before that char to see if its a number
                if (name[name.size()-1] == '-' && name.size() >= 2) {
                    pos = 2;
                }

                digit = name[name.size()-pos];
                char* p;
                std::strtoul(&digit, &p, 10);
                if (!(*p)) {
                    // If it is a number, clear the padding char
                    pad.clear();
                }
            }

        } else {

            frange = framesToFrameRange(seqInfo->frames, true, 0);
        }

        // Build the sequence string
        buf.str(root); // reset
        buf << name << frange << pad << ext;

        fs = FileSequence(buf.str(), style, &status);
        // TODO: Maybe don't bail on the entire loop
        // if we can't process this particular sequence.
        // Report the error by some means (logger, ...)
        // and continue collecting
        if (!status) {
            return status;
        }

        if (pad.empty()) {
            fs.setFrameSet(FrameSet());
        }

        // Save the FileSequence
        seqs.push_back(fs);
    }

    if (singleFiles) {
        // Copy the single file patterns
        seqs.insert(seqs.begin(), files.begin(), files.end());
    }

    return status;
}

} // fileseq
<commit_msg>Make a few symbols part of the internal namespace<commit_after>#include "fileseq.h"
#include "pad.h"
#include "private/frameset_p.h"
#include "private/sequence_p.h"
#include "private/strings.h"

#include <algorithm>
#include <cerrno>
#include <cstring>
#include <dirent.h>
#include <iterator>
#include <map>
#include <string>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utility>

namespace fileseq {

std::string framesToFrameRange(const Frames &frames,
                               bool sorted, int zfill) {

    Frames::size_type count = frames.size();
    if (count == 0) {
        return "";
    }

    if (count == 1) {
        return internal::zfill(frames[0], zfill);
    }

    Frames::const_iterator framesIt = frames.begin();
    Frames::const_iterator framesEnd = frames.end();

    Frames sortedFrames;

    if (sorted) {
        // Copy
        sortedFrames = frames;

        // Sort
        std::sort(sortedFrames.begin(), sortedFrames.end());

        framesIt = sortedFrames.begin();
        framesEnd = sortedFrames.end();
    }

    long step = 0;
    bool hasWritten = false;
    Frames::size_type i = 0;

    std::string start, end;
    std::stringstream buf;

    while (framesIt != framesEnd) {

        // Items left
        count = std::distance(framesIt, framesEnd);

        // If we get to the last element, just write it and end
        if (count <= 2) {
            for (i = 0; i < count; ++i) {
                if (hasWritten) {
                    buf << ",";
                }
                buf << internal::zfill(framesIt[i], zfill);
            }
            hasWritten = true;
            break;
        }

        // At this point, we have 3 or more frames to check.
        // Scan the current window of the slice to see how
        // many frames we can consume into a group
        step = framesIt[1] - framesIt[0];

        // Scan and update the pointer index until we get
        // to a point where the step changes
        for (i = 0; i < (count-1); ++i) {

            if ((framesIt[i+1] - framesIt[i]) != step) {
                // We have scanned as many frames as we can
                // for this group. Now write them and stop
                // looping on this window, because the step
                // has changed.
                break;
            }
        }

        // Subsequent groups are comma-separated
        if (hasWritten) {
            buf << ",";
            hasWritten = true;
        }

        // We only have a single frame to write for this group
        if (i == 0) {
            buf << internal::zfill(framesIt[0], zfill);
            framesIt++;
            hasWritten = true;
            continue;
        }

        // First do a check to see if we could have gotten a larger range
        // out of subsequent values with a different step size
        if (i == 1 && count > 3) {
            // Check if the next two pairwise frames have the same step.
            // If so, then it is better than our current grouping.
            if ((framesIt[2] - framesIt[1]) == (framesIt[3] - framesIt[2])) {
                // Just consume the first frame, and allow the next
                // loop to scan the new stepping
                buf << internal::zfill(framesIt[0], zfill);
                framesIt++;
                hasWritten = true;
                continue;
            }
        }

        // Otherwise write out this step range
        start = internal::zfill(framesIt[0], zfill);
        end = internal::zfill(framesIt[i], zfill);
        buf << start << "-" << end;

        if (step > 1) {
            buf << "x" << step;
        }

        hasWritten = true;

        // Advance our iterator to the end of the current window
        // so that we can test the next available number
        framesIt += (i+1);
    }

    return buf.str();
}

bool isFrameRange(const std::string &frange) {
    internal::RangeMatches matches;
    return frameRangeMatches(matches, frange);
}

FileSequence findSequenceOnDisk(const std::string &pattern, Status* ok) {
    return findSequenceOnDisk(pattern, PadStyleDefault, ok);
}

FileSequence findSequenceOnDisk(const std::string &pattern, PadStyle style, Status* ok) {
    if (ok != NULL) ok->clearError();

    Status localOk;
    FileSequence fs(pattern, style, &localOk);

    if (!localOk) {
        // Treat a bad pattern as a non-match
        std::cerr << "fileseq: " << localOk << std::endl;
        return FileSequence();
    }

    FileSequences seqs;
    localOk = findSequencesOnDisk(seqs, fs.dirname());
    if (!localOk) {
        if (ok != NULL) {
            std::ostringstream err;
            err << "Failed to find " << pattern << ": " << localOk;
            ok->setError(err.str());
        }
        return FileSequence();
    }

    const std::string &base = fs.basename();
    const std::string &ext = fs.ext();

    FileSequences::iterator it;
    for (it = seqs.begin(); it != seqs.end(); ++it) {
        // Find the first match and return it
        if (it->basename() == base && it->ext() == ext) {
            it->setPaddingStyle(style);
            return (*it);
        }
    }

    // If we get this far, we didn't find a match
    return FileSequence();
}

Status findSequencesOnDisk(FileSequences &seqs,
                           const std::string &path,
                           bool hiddenFiles,
                           bool singleFiles,
                           PadStyle style) {

    // Flags
    FindSequenceOpts opts = kNoOpt;
    if (hiddenFiles) {
        opts = opts | kOptHiddenFiles;
    }
    if (singleFiles) {
        opts = opts | kOptSingleFiles;
    }

    // Delegate
    return findSequencesOnDisk(seqs, path, opts, style);
}

namespace internal {

// Wrapper to make sure a DIR* is closed properly
// when it goes out of scope
class DirCloser {
public:
    DirCloser(DIR* d) : m_dir(d) {}

    ~DirCloser() {
        if (m_dir != NULL) {
            closedir(m_dir);
        }
    }

private:
    DIR* m_dir;
};

// Struct containing the ongoing parsing info
// about a sequence while reading a directory
struct SeqInfo {
    SeqInfo() : minWidth(0), padding(), frames() {}

    size_t minWidth;
    std::string padding;
    Frames frames;
};

// Key for sequence results map: basename, extension
typedef std::pair< const std::string, const std::string > SeqKey;
// Map type to track sequence results in a directory
typedef std::map< SeqKey, SeqInfo* > SeqsMap;

// Wrapper to make sure a SeqsMap is cleaned up properly
// when it goes out of scope
class SeqsMapCloser {
public:
    SeqsMapCloser(SeqsMap* m) : m_map(m) {}

    ~SeqsMapCloser() {
        if (m_map == NULL) {
            return;
        }

        SeqsMap::iterator it;
        for (it = m_map->begin(); it != m_map->end(); ++it) {
            if (it->second != NULL) {
                delete it->second;
                it->second = NULL;
            }
        }
    }

private:
    SeqsMap* m_map;
};

} // internal

Status findSequencesOnDisk(FileSequences &seqs,
                           const std::string &path,
                           FindSequenceOpts opts,
                           PadStyle style) {

    Status status;

    using namespace internal;

    // Options
    bool hiddenFiles = opts & kOptHiddenFiles;
    bool singleFiles = opts & kOptSingleFiles;

    // Open the directory
    DIR* dir;
    if ( (dir = opendir(path.c_str())) == NULL ) {
        status.setError(std::strerror(errno));
        return status;
    }

    DirCloser dirCloser(dir); // close when scope ends

    const PaddingMapper &padder = getPadMapperForStyle(style);

    // Track members of file sequences as we scan
    SeqsMap seqsMap;
    SeqsMapCloser seqsMapCloser(&seqsMap);

    // Track individual files (if option allows)
    FileSequences files;

    // Get our root search path and make sure it ends with
    // a path sep character
    std::string root = path;
    if ( !root.empty() && (root[root.size()-1] != strings::kPathSep) ) {
        root.append( 1, strings::kPathSep );
    }

    // Prep a string buffer that we can reuse to constantly build strings
    std::ostringstream buf(root, std::ios_base::out | std::ios_base::ate);

    Frame frame;
    size_t frameWidth = 0;
    std::string name;
    FileSequence fs;
    SeqInfo* seqInfo;
    SeqPatternMatch match;
    SeqsMap::iterator seqFound;

    struct stat f_stat;
    struct dirent* d_ent;

    errno = 0;

    // Read dir and sort files into groups
    while ( (d_ent = readdir(dir)) != NULL ) {

        // Is it a directory?
        if (d_ent->d_type == DT_DIR) {
            // Skip directories
            continue;
        }

        // Get the string name of the file
        name.assign(&(d_ent->d_name[0]));

        // Is it a hidden file?
        if (hiddenFiles && name[0] == '.' ) {
            continue;
        }

        // Is it a symlink?
        if (d_ent->d_type == DT_LNK) {

            // See if we are pointing to a directory
            buf << name;
            if ( stat(buf.str().c_str(), &f_stat) == -1 ) {

                std::stringstream err;
                err << "Error reading symlink " << buf.str()
                    << ": " << std::strerror(errno);

                // TODO: Maybe don't bail on the entire loop
                // if we can't read the symlink.
                // Report the error by some means (logger, ...)
                // and continue collecting

                status.setError(err.str());
                return status;
            }

            buf.str(root); // reset

            if (S_ISDIR(f_stat.st_mode)) {
                // Also skip symlinks that point to directories
                continue;
            }

        }

        // Are we matching a single frame (no range)?
        if (!getSingleFrameMatch(match, name)) {

            // Only keep it if we wanted single files
            if (singleFiles) {
                buf << name;

                fs = FileSequence(buf.str(), style, &status);
                if (!status) {
                    return status;
                }

                fs.setFrameSet(FrameSet());
                fs.setPadding("");
                files.push_back(fs);

                buf.str(root); // reset
            }

            continue;
        }

        char* pEnd;
        frame = Frame(std::strtol(match.range.c_str(), &pEnd, 10));

        frameWidth = match.range.size();

        SeqKey key = std::make_pair(match.base, match.ext);
        seqFound = seqsMap.find(key);

        if (seqFound == seqsMap.end()) {

            // Doesn't exist yet. Create new entry
            seqInfo = new SeqInfo;
            seqInfo->minWidth = frameWidth;
            seqInfo->padding = padder.getPaddingChars(frameWidth);
            seqsMap[key] = seqInfo;

        } else {

            // Exists
            seqInfo = seqFound->second;

            // Keep trying to find the smallest padding width
            // in the sequence
            if (frameWidth < seqInfo->minWidth) {
                seqInfo->minWidth = frameWidth;
                seqInfo->padding = padder.getPaddingChars(frameWidth);
            }
        }

        seqInfo->frames.push_back(frame);

        // Got what we needed. On to the next file, if available
        errno = 0;
    }

    // Any errors reading the directory listing?
    if ( errno != 0 ) {
        status.setError( std::strerror(errno) );
        return status;
    }

    // Now that the directory listing has been read,
    // do a pass on the collected groups of files to
    // convert them into sequence results

    seqs.reserve(seqsMap.size());

    size_t pos = 0;
    char digit;
    std::string ext, pad, frange;

    SeqsMap::const_iterator seqsIt;
    for (seqsIt = seqsMap.begin(); seqsIt != seqsMap.end(); ++seqsIt) {

        seqInfo = seqsIt->second;

        name = seqsIt->first.first;
        ext = seqsIt->first.second;
        pad = seqInfo->padding;

        if (seqInfo->frames.size() == 1) {

            buf.str("");
            buf << seqInfo->frames[0];
            frange = buf.str();

            if (!name.empty()) {
                // Make sure a non-sequence file doesn't accidentally
                // get reparsed as a range.
                pos = 1;
                // Check if the parsed number was preceded by a "-",
                // if so, check before that char to see if its a number
                if (name[name.size()-1] == '-' && name.size() >= 2) {
                    pos = 2;
                }

                digit = name[name.size()-pos];
                char* p;
                std::strtoul(&digit, &p, 10);
                if (!(*p)) {
                    // If it is a number, clear the padding char
                    pad.clear();
                }
            }

        } else {

            frange = framesToFrameRange(seqInfo->frames, true, 0);
        }

        // Build the sequence string
        buf.str(root); // reset
        buf << name << frange << pad << ext;

        fs = FileSequence(buf.str(), style, &status);
        // TODO: Maybe don't bail on the entire loop
        // if we can't process this particular sequence.
        // Report the error by some means (logger, ...)
        // and continue collecting
        if (!status) {
            return status;
        }

        if (pad.empty()) {
            fs.setFrameSet(FrameSet());
        }

        // Save the FileSequence
        seqs.push_back(fs);
    }

    if (singleFiles) {
        // Copy the single file patterns
        seqs.insert(seqs.begin(), files.begin(), files.end());
    }

    return status;
}

} // fileseq
<|endoftext|>


<commit_before>#include "Judge.h"
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace WinnerPoker;

std::vector<size_t> test(const std::vector<size_t> &t)
{
    std::stringstream ss;
    ss << "{ ";
    for (auto &&item : t)
    {
        ss << item << ", ";
    }
    ss.seekp(-2, ss.end);
    ss << " }" << std::endl;

    // std::cout << ss.str();

    std::vector<size_t> vector;
    auto                zip = Judge::getInstance().zip(t);
    for (auto &&x : zip)
    {
        for (int i = 1; i <= x.second; ++i)
        {
            vector.push_back((x.first << 8) | i);
        }
    }

    ss.str("");
    ss << "{ ";
    for (auto &&item : vector)
    {
        ss << item << ", ";
    }
    ss.seekp(-2, ss.end);
    ss << " }" << std::endl;

    // std::cout << ss.str();
    // std::cout << std::endl;
    return vector;
}

int main()
{

    // std::vector<size_t> hands = { 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 };
    // std::vector<size_t> hands = { 3, 3, 4, 6, 6, 7, 8, 10, 10, 11, 12, 13, 13, 14, 15 };
    // std::vector<size_t> hands = { 3, 3, 4, 4, 5, 5, 7, 7 };
    // std::vector<size_t> hands = { 3, 4, 5, 5, 5, 5, 7, 7, 8, 9, 10 };

    // std::vector<size_t> hands = { 3, 3, 3, 3, 4,  4,  4,  4,  5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,
    //                               8, 8, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15 };

    std::vector<size_t> tv = { 3, 3, 3, 4, 4, 4, 4, 5 };
    // std::vector<size_t> tv = { 3, 3, 3, 4, 4, 4 };
    tv = { 3, 3 };
    Judge::getInstance().setCurrentHandsCategory(test(tv), test(tv));

    // std::cout << Judge::getInstance().isPass(test(hands)) << std::endl;

    std::vector<size_t> hands;
    hands = { 3, 3, 3, 4, 5, 5, 5, 6, 6, 7 };
    hands = { 3, 3, 3, 4, 4, 5, 5, 7 };
    hands = { 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 3, 5, 6, 6, 7, 7, 8 };
    hands = { 3, 4, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 5, 5, 5, 6, 6 };
    hands = { 3, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 4, 5, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 5, 5, 5, 6, 6 };
    hands = { 3, 3, 3, 4, 4, 5, 5, 5, 6, 6 };
    hands = { 3, 3, 4, 4, 5, 5, 5, 6 };
    hands = { 3, 3, 3, 5, 14, 14, 14 };
    hands = { 3, 3, 3, 14, 14, 14 };
    hands = { 3, 3, 4, 4, 4, 5, 5, 5, 5, 6 };
    hands = { 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 8 };
    hands = { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 7 };
    hands = { 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 8 };
    hands = { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 7 };
    hands = { 3, 4, 4, 5, 5, 5, 5, 6 };
    hands = { 3, 4, 4, 5, 5, 5, 5, 6, 6 };
    hands = { 4, 5, 6, 6, 7, 8, 8, 8, 9, 9, 9, 12, 12, 12, 12 };
    hands = { 3, 3, 3, 4, 5, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 4, 5, 5, 5, 6 };
    hands = { 3, 4, 4, 5, 5, 5, 5, 6 };

#pragma mark - 跟牌提示
    // std::vector<size_t> hands;
    // hands = { 4, 4, 4, 5, 6, 6, 7, 7, 7, 15 };
    // hands = { 6, 6, 6, 6, 7, 7, 8, 8 };
    // hands = { 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 15 };
    // hands = { 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 15 };
    // hands = { 3, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8 };
    // hands = { 3, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8 };
    // hands = { 4, 5, 6, 6, 7, 8, 9, 10, 10, 11, 11, 12 };
    // hands = { 5, 5, 5, 5, 3 };
    // hands = { 12, 12, 12, 12 };
    //
    // std::vector<size_t> tv;
    // tv = { 3 };
    // tv = { 3, 3, 3, 4, 5 };
    // tv = { 3, 3, 3, 4, 5 };
    // tv = { 3, 3, 4, 4 };
    // tv = { 3 };
    // tv = { 3, 3, 3, 4, 4, 4, 5, 12, 12, 12 };
    // tv = { 3, 4, 5, 6, 7 };
    // tv = { 3, 3, 3, 4 };
    // tv = { 14 };
    //
    // Judge::getInstance().setCurrentHandsCategory(test(tv));

    auto              ret = Judge::getInstance().cardHint(test(hands));
    std::stringstream ss;

    for (auto &&item : ret)
    {
        ss.str("");

        ss << "{ ";
        for (auto &&element : item)
        {
            ss << element << ", ";
        }
        ss.seekp(-2, ss.end);
        ss << " }" << std::endl;

        // std::cout << ss.str();
    }

    for (auto &&item : ret)
    {
        ss.str("");

        ss << "{ ";
        for (auto &&element : item)
        {
            ss << (element >> 8) << ", ";
        }
        ss.seekp(-2, ss.end);
        ss << " }" << std::endl;

        std::cout << ss.str();
    }

    // for (int i = 0; i < 1; ++i)
    // {
    //     const auto &ret = Judge::getInstance().intentions(test(hands));
    //
    //     std::stringstream ss;
    //
    //     ss.str("");
    //     ss << "{ ";
    //     for (auto &&item : ret)
    //     {
    //         ss << (item >> 8) << ", ";
    //     }
    //     ss.seekp(-2, ss.end);
    //     ss << " }" << std::endl;
    //
    //     std::cout << ss.str();
    // }

    return 0;
}
<commit_msg>this doesn't really make things faster, but I tried<commit_after>#include "Judge.h"
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace WinnerPoker;

std::vector<size_t> test(const std::vector<size_t> &t)
{
    std::stringstream ss;
    ss << "{ ";
    for (auto &&item : t)
    {
        ss << item << ", ";
    }
    ss.seekp(-2, ss.end);
    ss << " }" << std::endl;

    // std::cout << ss.str();

    std::vector<size_t> vector;
    auto                zip = Judge::getInstance().zip(t);
    for (auto &&x : zip)
    {
        for (int i = 1; i <= x.second; ++i)
        {
            vector.push_back((x.first << 8) | i);
        }
    }

    ss.str("");
    ss << "{ ";
    for (auto &&item : vector)
    {
        ss << item << ", ";
    }
    ss.seekp(-2, ss.end);
    ss << " }" << std::endl;

    // std::cout << ss.str();
    // std::cout << std::endl;
    return vector;
}

int main()
{

    // std::vector<size_t> hands = { 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 };
    // std::vector<size_t> hands = { 3, 3, 4, 6, 6, 7, 8, 10, 10, 11, 12, 13, 13, 14, 15 };
    // std::vector<size_t> hands = { 3, 3, 4, 4, 5, 5, 7, 7 };
    // std::vector<size_t> hands = { 3, 4, 5, 5, 5, 5, 7, 7, 8, 9, 10 };

    // std::vector<size_t> hands = { 3, 3, 3, 3, 4,  4,  4,  4,  5,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,
    //                               8, 8, 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15 };

    std::vector<size_t> tv = { 3, 3, 3, 4, 4, 4, 4, 5 };
    // std::vector<size_t> tv = { 3, 3, 3, 4, 4, 4 };
    tv = { 3, 3 };
    Judge::getInstance().setCurrentHandsCategory(test(tv), test(tv));

    // std::cout << Judge::getInstance().isPass(test(hands)) << std::endl;

    std::vector<size_t> hands;
    hands = { 3, 3, 3, 4, 5, 5, 5, 6, 6, 7 };
    hands = { 3, 3, 3, 4, 4, 5, 5, 7 };
    hands = { 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 3, 5, 6, 6, 7, 7, 8 };
    hands = { 3, 4, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 5, 5, 5, 6, 6 };
    hands = { 3, 4, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 4, 5, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 5, 5, 5, 6, 6 };
    hands = { 3, 3, 3, 4, 4, 5, 5, 5, 6, 6 };
    hands = { 3, 3, 4, 4, 5, 5, 5, 6 };
    hands = { 3, 3, 3, 5, 14, 14, 14 };
    hands = { 3, 3, 3, 14, 14, 14 };
    hands = { 3, 3, 4, 4, 4, 5, 5, 5, 5, 6 };
    hands = { 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 8 };
    hands = { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 7 };
    hands = { 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 8 };
    hands = { 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 7 };
    hands = { 3, 4, 4, 5, 5, 5, 5, 6 };
    hands = { 3, 4, 4, 5, 5, 5, 5, 6, 6 };
    hands = { 4, 5, 6, 6, 7, 8, 8, 8, 9, 9, 9, 12, 12, 12, 12 };
    hands = { 3, 3, 3, 4, 5, 5, 5, 6, 6, 7 };
    hands = { 3, 4, 4, 5, 5, 5, 6 };
    hands = { 3, 4, 4, 5, 5, 5, 5, 6 };

#pragma mark - 跟牌提示
    // std::vector<size_t> hands;
    // hands = { 4, 4, 4, 5, 6, 6, 7, 7, 7, 15 };
    // hands = { 6, 6, 6, 6, 7, 7, 8, 8 };
    // hands = { 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 15 };
    // hands = { 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 15 };
    // hands = { 3, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8 };
    // hands = { 3, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8 };
    // hands = { 4, 5, 6, 6, 7, 8, 9, 10, 10, 11, 11, 12 };
    // hands = { 5, 5, 5, 5, 3 };
    // hands = { 12, 12, 12, 12 };
    //
    // std::vector<size_t> tv;
    // tv = { 3 };
    // tv = { 3, 3, 3, 4, 5 };
    // tv = { 3, 3, 3, 4, 5 };
    // tv = { 3, 3, 4, 4 };
    // tv = { 3 };
    // tv = { 3, 3, 3, 4, 4, 4, 5, 12, 12, 12 };
    // tv = { 3, 4, 5, 6, 7 };
    // tv = { 3, 3, 3, 4 };
    // tv = { 14 };
    //
    // Judge::getInstance().setCurrentHandsCategory(test(tv));

    auto              ret = Judge::getInstance().cardHint(test(hands));
    std::stringstream ss;

    for (auto &&item : ret)
    {
        ss.str("");

        ss << "{ ";
        for (auto &&element : item)
        {
            ss << element << ", ";
        }
        ss.seekp(-2, ss.end);
        ss << " }" << std::endl;

        // std::cout << ss.str();
    }

    for (auto &&item : ret)
    {
        ss.str("");

        ss << "{ ";
        for (auto &&element : item)
        {
            ss << (element >> 8) << ", ";
        }
        ss.seekp(-2, ss.end);
        ss << " }" << std::endl;

        // std::cout << ss.str();
    }

    // for (int i = 0; i < 1; ++i)
    // {
    //     const auto &ret = Judge::getInstance().intentions(test(hands));
    //
    //     std::stringstream ss;
    //
    //     ss.str("");
    //     ss << "{ ";
    //     for (auto &&item : ret)
    //     {
    //         ss << (item >> 8) << ", ";
    //     }
    //     ss.seekp(-2, ss.end);
    //     ss << " }" << std::endl;
    //
    //     std::cout << ss.str();
    // }

    std::vector<size_t> tttttt = { 3, 3, 3, 4, 4, 4, 4, 5 };
    std::vector<size_t> hhhhhh = { 3, 3, 3, 4, 4, 4, 4, 5 };
    // std::vector<size_t> tv = { 3, 3, 3, 4, 4, 4 };
    tttttt = {};
    hhhhhh = { 3, 3, 3, 3, 4, 4, 4, 4 };
    Judge::getInstance().setCurrentHandsCategory(test(tttttt), test(hhhhhh));
    auto r = Judge::getInstance().rearrangeHands(test(hhhhhh));

        ss.str("");
        ss << "{ ";
        for (auto &&item : r)
        {
            ss << (item >> 8) << ", ";
        }
        ss.seekp(-2, ss.end);
        ss << " }" << std::endl;

        std::cout << ss.str();

    return 0;
}
<|endoftext|>


<commit_before>#ifndef __GLYPH_HPP_INCLUDED
#define __GLYPH_HPP_INCLUDED

#include "font.hpp"
#include "model_common_functions.hpp"
#include "model_templates.hpp"
#include "species_or_glyph.hpp"

namespace model
{
    class Object;

    class Glyph
    {
        public:
            // constructor.
            Glyph(GlyphStruct glyph_struct);

            // destructor.
            ~Glyph();

            // this method sets a object pointer.
            void set_object_pointer(GLuint childID, void* parent_pointer);

            // this method gets a object ID and removes it from the `free_objectID_queue` if it was popped from the queue.
            GLuint get_objectID();

            glm::vec3 light_position;                    // light position.

            // The rest fields are created in the constructor.
            GLuint image_width;
            GLuint image_height;

            GLuint vertexPosition_modelspaceID;
            GLuint vertexUVID;
            GLuint vertexNormal_modelspaceID;

            std::vector<glm::vec3> vertices;       // vertices of the object.
            std::vector<glm::vec2> UVs;            // UVs of the object.
            std::vector<glm::vec3> normals;        // normals of the object.

            std::vector<GLuint> indices;           // the deleted vertices will be reused (though it is not required, if there's enough memory).
            std::vector<glm::vec3> indexed_vertices;
            std::vector<glm::vec2> indexed_UVs;
            std::vector<glm::vec3> indexed_normals;

            GLuint vertexbuffer;
            GLuint uvbuffer;
            GLuint normalbuffer;
            GLuint elementbuffer;

            model::Font *parent_pointer;             // pointer to the font.

            friend class Object;
            template<class T1>
                friend void render_children(std::vector<void*> &child_pointer_vector);
            template<class T1>
                friend void bind_child_to_parent(T1 child_pointer, std::vector<void*> &child_pointer_vector, std::queue<GLuint> &free_childID_queue);
            template<class T1>
                friend void render_species_or_glyph(T1 species_or_glyph_pointer);

        private:
            void bind_to_parent();

            // this method renders all objects of this species.
            void render();

            GLuint glyphID;                        // glyph ID, returned by `model::Font->get_glyphID()`.
            GLuint lightID;                        // light ID, returned by `glGetUniformLocation(programID, "LightPosition_worldspace");`.

            std::vector<void*> object_pointer_vector;
            std::queue<GLuint> free_objectID_queue;
    };
}

#endif
<commit_msg>`class Glyph`: `GLuint glyphID` -> `GLuint childID`.<commit_after>#ifndef __GLYPH_HPP_INCLUDED
#define __GLYPH_HPP_INCLUDED

#include "font.hpp"
#include "model_common_functions.hpp"
#include "model_templates.hpp"
#include "species_or_glyph.hpp"

namespace model
{
    class Object;

    class Glyph
    {
        public:
            // constructor.
            Glyph(GlyphStruct glyph_struct);

            // destructor.
            ~Glyph();

            // this method sets a object pointer.
            void set_object_pointer(GLuint childID, void* parent_pointer);

            // this method gets a object ID and removes it from the `free_objectID_queue` if it was popped from the queue.
            GLuint get_objectID();

            glm::vec3 light_position;                    // light position.

            // The rest fields are created in the constructor.
            GLuint image_width;
            GLuint image_height;

            GLuint vertexPosition_modelspaceID;
            GLuint vertexUVID;
            GLuint vertexNormal_modelspaceID;

            std::vector<glm::vec3> vertices;       // vertices of the object.
            std::vector<glm::vec2> UVs;            // UVs of the object.
            std::vector<glm::vec3> normals;        // normals of the object.

            std::vector<GLuint> indices;           // the deleted vertices will be reused (though it is not required, if there's enough memory).
            std::vector<glm::vec3> indexed_vertices;
            std::vector<glm::vec2> indexed_UVs;
            std::vector<glm::vec3> indexed_normals;

            GLuint vertexbuffer;
            GLuint uvbuffer;
            GLuint normalbuffer;
            GLuint elementbuffer;

            model::Font *parent_pointer;             // pointer to the font.

            friend class Object;
            template<class T1>
                friend void render_children(std::vector<void*> &child_pointer_vector);
            template<class T1>
                friend void bind_child_to_parent(T1 child_pointer, std::vector<void*> &child_pointer_vector, std::queue<GLuint> &free_childID_queue);
            template<class T1>
                friend void render_species_or_glyph(T1 species_or_glyph_pointer);

        private:
            void bind_to_parent();

            // this method renders all objects of this species.
            void render();

            GLuint childID;                        // glyph ID, returned by `model::Font->get_glyphID()`.
            GLuint lightID;                        // light ID, returned by `glGetUniformLocation(programID, "LightPosition_worldspace");`.

            std::vector<void*> object_pointer_vector;
            std::queue<GLuint> free_objectID_queue;
    };
}

#endif
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2010-2012 frankee zhou (frankee.zhou at gmail dot com)
 *
 * Distributed under under the Apache License, version 2.0 (the "License").
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

#include <cetty/craft/http/ServiceResponseMapping.h>

#include <boost/bind.hpp>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>

#include <cetty/logging/LoggerHelper.h>

#include <cetty/buffer/Unpooled.h>
#include <cetty/buffer/ChannelBuffer.h>
#include <cetty/handler/codec/http/HttpResponse.h>
#include <cetty/handler/codec/http/HttpRequest.h>
#include <cetty/handler/codec/http/HttpHeaders.h>
#include <cetty/protobuf/service/ProtobufUtil.h>
#include <cetty/protobuf/service/ProtobufService.h>
#include <cetty/protobuf/service/ProtobufServiceMessage.h>
#include <cetty/protobuf/service/ProtobufServiceRegister.h>
#include <cetty/protobuf/serialization/ProtobufFormatter.h>

#include <cetty/craft/craft.pb.h>

namespace cetty {
namespace craft {
namespace http {

using namespace google::protobuf;
using namespace cetty::protobuf::service;
using namespace cetty::protobuf::serialization;

static const int RESERVED_MIN_SIZE = 512;
static const int RESERVED_WRITE_SIZE = 12;
static const int RESERVED_AHEAD_WRITE_SIZE = 512;

ServiceResponseMapping::ServiceResponseMapping() {
    ProtobufServiceRegister::instance().registerServiceRegisteredCallback(
        boost::bind(&ServiceResponseMapping::onServiceRegistered,
                    this,
                    _1));
}

HttpResponsePtr ServiceResponseMapping::toHttpResponse(
    const HttpRequestPtr& req,
    const ProtobufServiceMessagePtr& message) {

    HttpResponsePtr response(new HttpResponse(HttpVersion::HTTP_1_1,
                             HttpResponseStatus::OK));

    std::string method = message->method();
    setHttpHeaders(method, response);

    const Message* paylod = message->payload();

    std::string producer = "json";

    if (paylod) {
        setHttpContent(*paylod, producer, response);
    }

    // for jsonp
    if (producer.compare("json") == 0) {
        std::string jquery = req->queryParameters().get("jsoncallback");

        if (!jquery.empty()) {
            jquery.append("(");
            response->content()->writeBytesAhead(jquery);
            response->content()->writeByte(')');
        }

        response->headers().addHeader(HttpHeaders::Names::CONTENT_TYPE, "application/x-javascript");
    }

    const std::string& uri = req->getUriString();

    if (uri.find(".png") != uri.npos) {
        response->headers().addHeader(HttpHeaders::Names::CONTENT_TYPE, "image/png");
    }

    // Decide whether to close the connection or not.
    bool keepAlive = req->keepAlive();

    if (keepAlive) {
        // Add 'Content-Length' header only for a keep-alive connection.
        LOG_DEBUG << "keep alive mode,set the content_length header";

        response->headers().setHeader(HttpHeaders::Names::CONNECTION,
                                      HttpHeaders::Values::KEEP_ALIVE);

        response->headers().setHeader(HttpHeaders::Names::CONTENT_LENGTH,
                                      response->content()->readableBytes());
    }
    else {
        response->headers().setHeader(HttpHeaders::Names::CONNECTION,
                                      HttpHeaders::Values::CLOSE);
    }

    return response;
}

void ServiceResponseMapping::setHttpContent(const Message& message,
        const std::string& producer,
        const HttpResponsePtr& response) {
    ChannelBufferPtr content;
    const CraftMessageOptions& options =
        message.GetDescriptor()->options().GetExtension(craft_message_options);

    if (options.has_raw_field()) {
        const FieldDescriptor* field =
            message.GetDescriptor()->FindFieldByName(options.raw_field());
        const Reflection* reflection = message.GetReflection();

        const CraftFieldOptions& fieldOptions =
            field->options().GetExtension(craft_options);

        BOOST_ASSERT(field);

        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
            const Message& msg = reflection->GetMessage(message, field);

            if (fieldOptions.has_raw_field()) {
                const FieldDescriptor* fieldfield =
                    msg.GetDescriptor()->FindFieldByName(fieldOptions.raw_field());
                const Reflection* fieldReflection = msg.GetReflection();

                const std::string& fieldContent =
                    fieldReflection->GetStringReference(msg, fieldfield, NULL);

                content = Unpooled::buffer(fieldContent.size(),
                                           RESERVED_AHEAD_WRITE_SIZE);
                content->writeBytes(fieldContent);
                response->setContent(content);
                return;
            }
        }
        else {

        }
    }

    ProtobufFormatter* formatter = ProtobufFormatter::getFormatter(producer);

    if (!formatter) {
        //
        //response->setStatus(HttpResponseStatus::NOT_FOUND);
    }

    if (options.has_entity_field()) {
        const FieldDescriptor* field =
            message.GetDescriptor()->FindFieldByName(options.entity_field());
        const Reflection* reflection = message.GetReflection();
        BOOST_ASSERT(field);

        if (field->is_repeated()) {
            switch (field->type()) {
            case FieldDescriptor::TYPE_INT64:
                content = Unpooled::buffer(
                              RESERVED_WRITE_SIZE,
                              RESERVED_AHEAD_WRITE_SIZE);

                //formatter->format(field->camelcase_name(), , content);
                break;

            case FieldDescriptor::CPPTYPE_DOUBLE:
                //                 content = Unpooled::buffer(
                //                               fieldName.size() + 32 * value.size() + RESERVED_WRITE_SIZE,
                //                               RESERVED_AHEAD_WRITE_SIZE);
                //
                //                 formatter->format(fieldName, value, content);
                break;

            default:
                break;
            }
        }
        else {
            switch (field->type()) {
            case FieldDescriptor::TYPE_INT64:
                //                 content = Unpooled::buffer(
                //                               fieldName.size() + 16 + RESERVED_WRITE_SIZE,
                //                               RESERVED_AHEAD_WRITE_SIZE);
                //
                //                 formatter->format(fieldName, value, content);

                break;

            case FieldDescriptor::TYPE_DOUBLE:
                //                 content = Unpooled::buffer(
                //                               fieldName.size() + 32 + RESERVED_WRITE_SIZE,
                //                               RESERVED_AHEAD_WRITE_SIZE);
                //
                //                 formatter->format(fieldName, value, content);

                break;

            case FieldDescriptor::TYPE_BYTES:
                break;

            case FieldDescriptor::TYPE_STRING:
                //                 if (value) {
                //                     content = Unpooled::buffer(
                //                                   value->size() + fieldName.size() + RESERVED_WRITE_SIZE,
                //                                   RESERVED_AHEAD_WRITE_SIZE);
                //
                //                     formatter->format(fieldName, *value, content);
                //                 }

                break;

            case FieldDescriptor::TYPE_MESSAGE:
                const Message& msg = reflection->GetMessage(message, field);

                int size = msg.ByteSize();
                content = Unpooled::buffer(size + RESERVED_MIN_SIZE * 4,
                                           RESERVED_AHEAD_WRITE_SIZE);

                formatter->format(msg, content);

                break;
            }
        }
    }
    else {
        content = Unpooled::buffer(RESERVED_MIN_SIZE * 4,
                                   RESERVED_AHEAD_WRITE_SIZE);

        formatter->format(message, content);
    }

    response->setContent(content);
}

void ServiceResponseMapping::setHttpHeaders(const std::string& method,
        const HttpResponsePtr& response) {

    //response->headers().
}

void ServiceResponseMapping::onServiceRegistered(const ProtobufServicePtr& service) {
    const ServiceDescriptor* descriptor = service->GetDescriptor();

    int methodCount = descriptor->method_count();

    for (int i = 0; i < methodCount; ++i) {
        const MethodDescriptor* method = descriptor->method(i);
        const MethodOptions& options = method->options();

        if (options.HasExtension(craft_method_options)) {
            const CraftMethodOptions& methodOptions =
                options.GetExtension(craft_method_options);

            parseHeaders(methodOptions.headers(), &headers_[method->full_name()]);
        }
    }
}

void ServiceResponseMapping::parseHeaders(const std::string& headerStr,
        NameValueCollection* headers) {

}

ServiceResponseMapping& ServiceResponseMapping::instance() {
    if (!mapping_) {
        mapping_ = new ServiceResponseMapping;
    }

    return *mapping_;
}

ServiceResponseMapping* ServiceResponseMapping::mapping_ = NULL;

}
}
}
<commit_msg>对于png，添加较长的生命周期<commit_after>/**
 * Copyright (c) 2010-2012 frankee zhou (frankee.zhou at gmail dot com)
 *
 * Distributed under under the Apache License, version 2.0 (the "License").
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

#include <cetty/craft/http/ServiceResponseMapping.h>

#include <boost/bind.hpp>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>

#include <cetty/logging/LoggerHelper.h>

#include <cetty/buffer/Unpooled.h>
#include <cetty/buffer/ChannelBuffer.h>
#include <cetty/handler/codec/http/HttpResponse.h>
#include <cetty/handler/codec/http/HttpRequest.h>
#include <cetty/handler/codec/http/HttpHeaders.h>
#include <cetty/protobuf/service/ProtobufUtil.h>
#include <cetty/protobuf/service/ProtobufService.h>
#include <cetty/protobuf/service/ProtobufServiceMessage.h>
#include <cetty/protobuf/service/ProtobufServiceRegister.h>
#include <cetty/protobuf/serialization/ProtobufFormatter.h>

#include <cetty/craft/craft.pb.h>

namespace cetty {
namespace craft {
namespace http {

using namespace google::protobuf;
using namespace cetty::protobuf::service;
using namespace cetty::protobuf::serialization;

static const int RESERVED_MIN_SIZE = 512;
static const int RESERVED_WRITE_SIZE = 12;
static const int RESERVED_AHEAD_WRITE_SIZE = 512;

ServiceResponseMapping::ServiceResponseMapping() {
    ProtobufServiceRegister::instance().registerServiceRegisteredCallback(
        boost::bind(&ServiceResponseMapping::onServiceRegistered,
                    this,
                    _1));
}

HttpResponsePtr ServiceResponseMapping::toHttpResponse(
    const HttpRequestPtr& req,
    const ProtobufServiceMessagePtr& message) {

    HttpResponsePtr response(new HttpResponse(HttpVersion::HTTP_1_1,
                             HttpResponseStatus::OK));

    std::string method = message->method();
    setHttpHeaders(method, response);

    const Message* paylod = message->payload();

    std::string producer = "json";

    if (paylod) {
        setHttpContent(*paylod, producer, response);
    }

    // for jsonp
    if (producer.compare("json") == 0) {
        std::string jquery = req->queryParameters().get("jsoncallback");

        if (!jquery.empty()) {
            jquery.append("(");
            response->content()->writeBytesAhead(jquery);
            response->content()->writeByte(')');
        }

        response->headers().addHeader(HttpHeaders::Names::CONTENT_TYPE, "application/x-javascript");
    }

    const std::string& uri = req->getUriString();

    if (uri.find(".png") != uri.npos) {
        response->headers().addHeader(HttpHeaders::Names::CONTENT_TYPE, "image/png");
        response->headers().addHeader(HttpHeaders::Names::CACHE_CONTROL, "max-age=2592000");
    }

    // Decide whether to close the connection or not.
    bool keepAlive = req->keepAlive();

    if (keepAlive) {
        // Add 'Content-Length' header only for a keep-alive connection.
        LOG_DEBUG << "keep alive mode,set the content_length header";

        response->headers().setHeader(HttpHeaders::Names::CONNECTION,
                                      HttpHeaders::Values::KEEP_ALIVE);

        response->headers().setHeader(HttpHeaders::Names::CONTENT_LENGTH,
                                      response->content()->readableBytes());
    }
    else {
        response->headers().setHeader(HttpHeaders::Names::CONNECTION,
                                      HttpHeaders::Values::CLOSE);
    }

    return response;
}

void ServiceResponseMapping::setHttpContent(const Message& message,
        const std::string& producer,
        const HttpResponsePtr& response) {
    ChannelBufferPtr content;
    const CraftMessageOptions& options =
        message.GetDescriptor()->options().GetExtension(craft_message_options);

    if (options.has_raw_field()) {
        const FieldDescriptor* field =
            message.GetDescriptor()->FindFieldByName(options.raw_field());
        const Reflection* reflection = message.GetReflection();

        const CraftFieldOptions& fieldOptions =
            field->options().GetExtension(craft_options);

        BOOST_ASSERT(field);

        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
            const Message& msg = reflection->GetMessage(message, field);

            if (fieldOptions.has_raw_field()) {
                const FieldDescriptor* fieldfield =
                    msg.GetDescriptor()->FindFieldByName(fieldOptions.raw_field());
                const Reflection* fieldReflection = msg.GetReflection();

                const std::string& fieldContent =
                    fieldReflection->GetStringReference(msg, fieldfield, NULL);

                content = Unpooled::buffer(fieldContent.size(),
                                           RESERVED_AHEAD_WRITE_SIZE);
                content->writeBytes(fieldContent);
                response->setContent(content);
                return;
            }
        }
        else {

        }
    }

    ProtobufFormatter* formatter = ProtobufFormatter::getFormatter(producer);

    if (!formatter) {
        //
        //response->setStatus(HttpResponseStatus::NOT_FOUND);
    }

    if (options.has_entity_field()) {
        const FieldDescriptor* field =
            message.GetDescriptor()->FindFieldByName(options.entity_field());
        const Reflection* reflection = message.GetReflection();
        BOOST_ASSERT(field);

        if (field->is_repeated()) {
            switch (field->type()) {
            case FieldDescriptor::TYPE_INT64:
                content = Unpooled::buffer(
                              RESERVED_WRITE_SIZE,
                              RESERVED_AHEAD_WRITE_SIZE);

                //formatter->format(field->camelcase_name(), , content);
                break;

            case FieldDescriptor::CPPTYPE_DOUBLE:
                //                 content = Unpooled::buffer(
                //                               fieldName.size() + 32 * value.size() + RESERVED_WRITE_SIZE,
                //                               RESERVED_AHEAD_WRITE_SIZE);
                //
                //                 formatter->format(fieldName, value, content);
                break;

            default:
                break;
            }
        }
        else {
            switch (field->type()) {
            case FieldDescriptor::TYPE_INT64:
                //                 content = Unpooled::buffer(
                //                               fieldName.size() + 16 + RESERVED_WRITE_SIZE,
                //                               RESERVED_AHEAD_WRITE_SIZE);
                //
                //                 formatter->format(fieldName, value, content);

                break;

            case FieldDescriptor::TYPE_DOUBLE:
                //                 content = Unpooled::buffer(
                //                               fieldName.size() + 32 + RESERVED_WRITE_SIZE,
                //                               RESERVED_AHEAD_WRITE_SIZE);
                //
                //                 formatter->format(fieldName, value, content);

                break;

            case FieldDescriptor::TYPE_BYTES:
                break;

            case FieldDescriptor::TYPE_STRING:
                //                 if (value) {
                //                     content = Unpooled::buffer(
                //                                   value->size() + fieldName.size() + RESERVED_WRITE_SIZE,
                //                                   RESERVED_AHEAD_WRITE_SIZE);
                //
                //                     formatter->format(fieldName, *value, content);
                //                 }

                break;

            case FieldDescriptor::TYPE_MESSAGE:
                const Message& msg = reflection->GetMessage(message, field);

                int size = msg.ByteSize();
                content = Unpooled::buffer(size + RESERVED_MIN_SIZE * 4,
                                           RESERVED_AHEAD_WRITE_SIZE);

                formatter->format(msg, content);

                break;
            }
        }
    }
    else {
        content = Unpooled::buffer(RESERVED_MIN_SIZE * 4,
                                   RESERVED_AHEAD_WRITE_SIZE);

        formatter->format(message, content);
    }

    response->setContent(content);
}

void ServiceResponseMapping::setHttpHeaders(const std::string& method,
        const HttpResponsePtr& response) {

    //response->headers().
}

void ServiceResponseMapping::onServiceRegistered(const ProtobufServicePtr& service) {
    const ServiceDescriptor* descriptor = service->GetDescriptor();

    int methodCount = descriptor->method_count();

    for (int i = 0; i < methodCount; ++i) {
        const MethodDescriptor* method = descriptor->method(i);
        const MethodOptions& options = method->options();

        if (options.HasExtension(craft_method_options)) {
            const CraftMethodOptions& methodOptions =
                options.GetExtension(craft_method_options);

            parseHeaders(methodOptions.headers(), &headers_[method->full_name()]);
        }
    }
}

void ServiceResponseMapping::parseHeaders(const std::string& headerStr,
        NameValueCollection* headers) {

}

ServiceResponseMapping& ServiceResponseMapping::instance() {
    if (!mapping_) {
        mapping_ = new ServiceResponseMapping;
    }

    return *mapping_;
}

ServiceResponseMapping* ServiceResponseMapping::mapping_ = NULL;

}
}
}
<|endoftext|>