/******************************************************************************
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  Implements interface to MapInfo .ID files used as attribute
 *           indexes.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 2003, Frank Warmerdam
 * Copyright (c) 2008-2010, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "ogr_attrind.h"
#include "mitab/mitab_priv.h"
#include "cpl_minixml.h"

CPL_CVSID("$Id: ogr_miattrind.cpp 37371 2017-02-13 11:41:59Z rouault $");

/************************************************************************/
/*                            OGRMIAttrIndex                            */
/*                                                                      */
/*      MapInfo .ID implementation of access to one fields              */
/*      indexing.                                                       */
/************************************************************************/

class OGRMILayerAttrIndex;

class OGRMIAttrIndex : public OGRAttrIndex
{
public:
    int         iIndex;
    TABINDFile  *poINDFile;
    OGRMILayerAttrIndex *poLIndex;
    OGRFieldDefn *poFldDefn;

    int         iField;

                OGRMIAttrIndex( OGRMILayerAttrIndex *, int iIndex, int iField);
               ~OGRMIAttrIndex();

    GByte      *BuildKey( OGRField *psKey );
    GIntBig     GetFirstMatch( OGRField *psKey ) override;
    GIntBig    *GetAllMatches( OGRField *psKey ) override;
    GIntBig    *GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength ) override;

    OGRErr      AddEntry( OGRField *psKey, GIntBig nFID ) override;
    OGRErr      RemoveEntry( OGRField *psKey, GIntBig nFID ) override;

    OGRErr      Clear() override;
};

/************************************************************************/
/* ==================================================================== */
/*                         OGRMILayerAttrIndex                          */
/*                                                                      */
/*      MapInfo .ID specific implementation of a layer attribute        */
/*      index.                                                          */
/* ==================================================================== */
/************************************************************************/

class OGRMILayerAttrIndex : public OGRLayerAttrIndex
{
public:
    TABINDFile  *poINDFile;

    int         nIndexCount;
    OGRMIAttrIndex **papoIndexList;

    char        *pszMetadataFilename;
    char        *pszMIINDFilename;

    int         bINDAsReadOnly;
    int         bUnlinkINDFile;

                OGRMILayerAttrIndex();
    virtual     ~OGRMILayerAttrIndex();

    /* base class virtual methods */
    OGRErr      Initialize( const char *pszIndexPath, OGRLayer * ) override;
    OGRErr      CreateIndex( int iField ) override;
    OGRErr      DropIndex( int iField ) override;
    OGRErr      IndexAllFeatures( int iField = -1 ) override;

    OGRErr      AddToIndex( OGRFeature *poFeature, int iField = -1 ) override;
    OGRErr      RemoveFromIndex( OGRFeature *poFeature ) override;

    OGRAttrIndex *GetFieldIndex( int iField ) override;

    /* custom to OGRMILayerAttrIndex */
    OGRErr      SaveConfigToXML();
    OGRErr      LoadConfigFromXML();
    OGRErr      LoadConfigFromXML(const char* pszRawXML);
    void        AddAttrInd( int iField, int iINDIndex );

    OGRLayer   *GetLayer() { return poLayer; }
};

/************************************************************************/
/*                        OGRMILayerAttrIndex()                         */
/************************************************************************/

OGRMILayerAttrIndex::OGRMILayerAttrIndex() :
    poINDFile(NULL),
    nIndexCount(0),
    papoIndexList(NULL),
    pszMetadataFilename(NULL),
    pszMIINDFilename(NULL),
    bINDAsReadOnly(TRUE),
    bUnlinkINDFile(FALSE)
{}

/************************************************************************/
/*                        ~OGRMILayerAttrIndex()                        */
/************************************************************************/

OGRMILayerAttrIndex::~OGRMILayerAttrIndex()

{
    if( poINDFile != NULL )
    {
        poINDFile->Close();
        delete poINDFile;
        poINDFile = NULL;
    }

    if (bUnlinkINDFile)
        VSIUnlink( pszMIINDFilename );

    for( int i = 0; i < nIndexCount; i++ )
        delete papoIndexList[i];
    CPLFree( papoIndexList );

    CPLFree( pszMIINDFilename );
    CPLFree( pszMetadataFilename );
}

/************************************************************************/
/*                             Initialize()                             */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::Initialize( const char *pszIndexPathIn,
                                        OGRLayer *poLayerIn )

{
    if( poLayerIn == poLayer )
        return OGRERR_NONE;

/* -------------------------------------------------------------------- */
/*      Capture input information and form static pathnames.            */
/* -------------------------------------------------------------------- */
    poLayer = poLayerIn;

    pszIndexPath = CPLStrdup( pszIndexPathIn );

    /* try to process the XML string directly */
    if (STARTS_WITH_CI(pszIndexPathIn, "<OGRMILayerAttrIndex>"))
        return LoadConfigFromXML(pszIndexPathIn);

    pszMetadataFilename = CPLStrdup(
        CPLResetExtension( pszIndexPathIn, "idm" ) );

    pszMIINDFilename = CPLStrdup(CPLResetExtension( pszIndexPathIn, "ind" ));

/* -------------------------------------------------------------------- */
/*      If a metadata file already exists, load it.                     */
/* -------------------------------------------------------------------- */
    OGRErr eErr;
    VSIStatBuf sStat;

    if( VSIStat( pszMetadataFilename, &sStat ) == 0 )
    {
        eErr = LoadConfigFromXML();
        if( eErr != OGRERR_NONE )
            return eErr;
    }

    return OGRERR_NONE;
}

/************************************************************************/
/*                         LoadConfigFromXML()                          */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::LoadConfigFromXML(const char* pszRawXML)

{
/* -------------------------------------------------------------------- */
/*      Parse the XML.                                                  */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psRoot = CPLParseXMLString( pszRawXML );

    if( psRoot == NULL )
        return OGRERR_FAILURE;

/* -------------------------------------------------------------------- */
/*      Open the index file.                                            */
/* -------------------------------------------------------------------- */
    poINDFile = new TABINDFile();

    if (pszMIINDFilename == NULL)
        pszMIINDFilename = CPLStrdup(CPLGetXMLValue(psRoot,"MIIDFilename",""));

    if( pszMIINDFilename == NULL )
        return OGRERR_FAILURE;

    /* NOTE: Replaced r+ with r according to explanation in Ticket #1620.
     * This change has to be observed if it doesn't cause any
     * problems in future. (mloskot)
     */
    if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 )
    {
        CPLDestroyXMLNode( psRoot );
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Failed to open index file %s.",
                  pszMIINDFilename );
        return OGRERR_FAILURE;
    }
/* -------------------------------------------------------------------- */
/*      Process each attrindex.                                         */
/* -------------------------------------------------------------------- */
    for( CPLXMLNode *psAttrIndex = psRoot->psChild;
         psAttrIndex != NULL;
         psAttrIndex = psAttrIndex->psNext )
    {
        if( psAttrIndex->eType != CXT_Element
            || !EQUAL(psAttrIndex->pszValue,"OGRMIAttrIndex") )
            continue;

        int iField = atoi(CPLGetXMLValue(psAttrIndex,"FieldIndex","-1"));
        int iIndexIndex = atoi(CPLGetXMLValue(psAttrIndex,"IndexIndex","-1"));

        if( iField == -1 || iIndexIndex == -1 )
        {
            CPLError( CE_Warning, CPLE_AppDefined,
                      "Skipping corrupt OGRMIAttrIndex entry." );
            continue;
        }

        AddAttrInd( iField, iIndexIndex );
    }

    CPLDestroyXMLNode( psRoot );

    CPLDebug( "OGR", "Restored %d field indexes for layer %s from %s on %s.",
              nIndexCount, poLayer->GetLayerDefn()->GetName(),
              pszMetadataFilename ? pszMetadataFilename : "--unknown--",
              pszMIINDFilename );

    return OGRERR_NONE;
}

OGRErr OGRMILayerAttrIndex::LoadConfigFromXML()
{
    CPLAssert( poINDFile == NULL );

/* -------------------------------------------------------------------- */
/*      Read the XML file.                                              */
/* -------------------------------------------------------------------- */
    VSILFILE *fp = VSIFOpenL( pszMetadataFilename, "rb" );
    if( fp == NULL )
        return OGRERR_FAILURE;

    if( VSIFSeekL( fp, 0, SEEK_END ) != 0 )
    {
        VSIFCloseL(fp);
        return OGRERR_FAILURE;
    }
    const vsi_l_offset nXMLSize = VSIFTellL( fp );
    if( nXMLSize > 10 * 1024 * 1024 ||
        VSIFSeekL( fp, 0, SEEK_SET ) != 0 )
    {
        VSIFCloseL(fp);
        return OGRERR_FAILURE;
    }

    char *pszRawXML = (char *) CPLMalloc((size_t)nXMLSize+1);
    pszRawXML[nXMLSize] = '\0';
    if( VSIFReadL( pszRawXML, (size_t)nXMLSize, 1, fp ) != 1 )
    {
        VSIFCloseL(fp);
        return OGRERR_FAILURE;
    }

    VSIFCloseL( fp );

    OGRErr eErr = LoadConfigFromXML(pszRawXML);
    CPLFree(pszRawXML);

    return eErr;
}

/************************************************************************/
/*                          SaveConfigToXML()                           */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::SaveConfigToXML()

{
    if( nIndexCount == 0 )
        return OGRERR_NONE;

/* -------------------------------------------------------------------- */
/*      Create the XML tree corresponding to this layer.                */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psRoot =
        CPLCreateXMLNode( NULL, CXT_Element, "OGRMILayerAttrIndex" );

    CPLCreateXMLElementAndValue( psRoot, "MIIDFilename",
                                 CPLGetFilename( pszMIINDFilename ) );

    for( int i = 0; i < nIndexCount; i++ )
    {
        OGRMIAttrIndex *poAI = papoIndexList[i];
        CPLXMLNode *psIndex =
            CPLCreateXMLNode( psRoot, CXT_Element, "OGRMIAttrIndex" );

        CPLCreateXMLElementAndValue( psIndex, "FieldIndex",
                                     CPLSPrintf( "%d", poAI->iField ) );

        CPLCreateXMLElementAndValue( psIndex, "FieldName",
                                     poLayer->GetLayerDefn()->GetFieldDefn(poAI->iField)->GetNameRef() );

        CPLCreateXMLElementAndValue( psIndex, "IndexIndex",
                                     CPLSPrintf( "%d", poAI->iIndex ) );
    }

/* -------------------------------------------------------------------- */
/*      Save it.                                                        */
/* -------------------------------------------------------------------- */
    char *pszRawXML = CPLSerializeXMLTree( psRoot );

    CPLDestroyXMLNode( psRoot );

    FILE *fp = VSIFOpen( pszMetadataFilename, "wb" );
    if( fp == NULL )
    {
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Failed to pen `%s' for write.",
                  pszMetadataFilename );
        CPLFree( pszRawXML );
        return OGRERR_FAILURE;
    }

    OGRErr eErr = (VSIFWrite( pszRawXML, strlen(pszRawXML), 1, fp ) == 1) ? OGRERR_NONE : OGRERR_FAILURE;
    VSIFClose( fp );

    CPLFree( pszRawXML );

    return eErr;
}

/************************************************************************/
/*                          IndexAllFeatures()                          */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::IndexAllFeatures( int iField )

{
    poLayer->ResetReading();

    OGRFeature *poFeature = NULL;
    while( (poFeature = poLayer->GetNextFeature()) != NULL )
    {
        const OGRErr eErr = AddToIndex( poFeature, iField );

        delete poFeature;

        if( eErr != OGRERR_NONE )
            return eErr;
    }

    poLayer->ResetReading();

    return OGRERR_NONE;
}

/************************************************************************/
/*                            CreateIndex()                             */
/*                                                                      */
/*      Create an index corresponding to the indicated field, but do    */
/*      not populate it.  Use IndexAllFeatures() for that.              */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::CreateIndex( int iField )

{
/* -------------------------------------------------------------------- */
/*      Do we have an open .ID file yet?  If not, create it now.        */
/* -------------------------------------------------------------------- */
    if( poINDFile == NULL )
    {
        poINDFile = new TABINDFile();
        if( poINDFile->Open( pszMIINDFilename, "w+" ) != 0 )
        {
            delete poINDFile;
            poINDFile = NULL;

            CPLError( CE_Failure, CPLE_OpenFailed,
                      "Failed to create %s.",
                      pszMIINDFilename );
            return OGRERR_FAILURE;
        }
    }
    else if (bINDAsReadOnly)
    {
        poINDFile->Close();
        if( poINDFile->Open( pszMIINDFilename, "r+" ) != 0 )
        {
            CPLError( CE_Failure, CPLE_OpenFailed,
                      "Failed to open %s as write-only.",
                      pszMIINDFilename );

            if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 )
            {
                CPLError( CE_Failure, CPLE_OpenFailed,
                      "Cannot re-open %s as read-only.",
                      pszMIINDFilename );
                delete poINDFile;
                poINDFile = NULL;
            }

            return OGRERR_FAILURE;
        }
        else
        {
            bINDAsReadOnly = FALSE;
        }
    }

/* -------------------------------------------------------------------- */
/*      Do we have this field indexed already?                          */
/* -------------------------------------------------------------------- */
    OGRFieldDefn *poFldDefn=poLayer->GetLayerDefn()->GetFieldDefn(iField);

    for( int i = 0; i < nIndexCount; i++ )
    {
        if( papoIndexList[i]->iField == iField )
        {
            CPLError( CE_Failure, CPLE_AppDefined,
                      "It seems we already have an index for field %d/%s\n"
                      "of layer %s.",
                      iField, poFldDefn->GetNameRef(),
                      poLayer->GetLayerDefn()->GetName() );
            return OGRERR_FAILURE;
        }
    }

/* -------------------------------------------------------------------- */
/*      What is the corresponding field type in TAB?  Note that we      */
/*      don't allow indexing of any of the list types.                  */
/* -------------------------------------------------------------------- */
    TABFieldType eTABFT;
    int           nFieldWidth = 0;

    switch( poFldDefn->GetType() )
    {
      case OFTInteger:
        eTABFT = TABFInteger;
        break;

      case OFTReal:
        eTABFT = TABFFloat;
        break;

      case OFTString:
        eTABFT = TABFChar;
        if( poFldDefn->GetWidth() > 0 )
            nFieldWidth = poFldDefn->GetWidth();
        else
            nFieldWidth = 64;
        break;

      default:
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Indexing not support for the field type of field %s.",
                  poFldDefn->GetNameRef() );
        return OGRERR_FAILURE;
    }

/* -------------------------------------------------------------------- */
/*      Create the index.                                               */
/* -------------------------------------------------------------------- */
    const int iINDIndex = poINDFile->CreateIndex( eTABFT, nFieldWidth );

    // CreateIndex() reports it's own errors.
    if( iINDIndex < 0 )
        return OGRERR_FAILURE;

    AddAttrInd( iField, iINDIndex );

    bUnlinkINDFile = FALSE;

/* -------------------------------------------------------------------- */
/*      Save the new configuration.                                     */
/* -------------------------------------------------------------------- */
    return SaveConfigToXML();
}

/************************************************************************/
/*                             DropIndex()                              */
/*                                                                      */
/*      For now we don't have any capability to remove index data       */
/*      from the MapInfo index file, so we just limit ourselves to      */
/*      ignoring it from now on.                                        */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::DropIndex( int iField )

{
/* -------------------------------------------------------------------- */
/*      Do we have this field indexed already?                          */
/* -------------------------------------------------------------------- */
    OGRFieldDefn *poFldDefn=poLayer->GetLayerDefn()->GetFieldDefn(iField);

    int i = 0;
    for( ; i < nIndexCount; i++ )
    {
        if( papoIndexList[i]->iField == iField )
            break;
    }

    if( i == nIndexCount )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "DROP INDEX on field (%s) that doesn't have an index.",
                  poFldDefn->GetNameRef() );
        return OGRERR_FAILURE;
    }

/* -------------------------------------------------------------------- */
/*      Remove from the list.                                           */
/* -------------------------------------------------------------------- */
    OGRMIAttrIndex *poAI = papoIndexList[i];

    memmove( papoIndexList + i, papoIndexList + i + 1,
             sizeof(void*) * (nIndexCount - i - 1) );

    delete poAI;

    nIndexCount--;

/* -------------------------------------------------------------------- */
/*      Save the new configuration, or if there is nothing left try     */
/*      to clean up the index files.                                    */
/* -------------------------------------------------------------------- */

    if( nIndexCount > 0 )
        return SaveConfigToXML();
    else
    {
        bUnlinkINDFile = TRUE;
        VSIUnlink( pszMetadataFilename );

        return OGRERR_NONE;
    }
}

/************************************************************************/
/*                             AddAttrInd()                             */
/************************************************************************/

void OGRMILayerAttrIndex::AddAttrInd( int iField, int iINDIndex )

{
    OGRMIAttrIndex *poAttrInd = new OGRMIAttrIndex( this, iINDIndex, iField);

    nIndexCount++;
    papoIndexList = (OGRMIAttrIndex **)
        CPLRealloc(papoIndexList, sizeof(void*) * nIndexCount);

    papoIndexList[nIndexCount-1] = poAttrInd;
}

/************************************************************************/
/*                         GetFieldAttrIndex()                          */
/************************************************************************/

OGRAttrIndex *OGRMILayerAttrIndex::GetFieldIndex( int iField )

{
    for( int i = 0; i < nIndexCount; i++ )
    {
        if( papoIndexList[i]->iField == iField )
            return papoIndexList[i];
    }

    return NULL;
}

/************************************************************************/
/*                             AddToIndex()                             */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::AddToIndex( OGRFeature *poFeature,
                                        int iTargetField )

{
    OGRErr eErr = OGRERR_NONE;

    if( poFeature->GetFID() == OGRNullFID )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Attempt to index feature with no FID." );
        return OGRERR_FAILURE;
    }

    for( int i = 0; i < nIndexCount && eErr == OGRERR_NONE; i++ )
    {
        int iField = papoIndexList[i]->iField;

        if( iTargetField != -1 && iTargetField != iField )
            continue;

        if( !poFeature->IsFieldSetAndNotNull( iField ) )
            continue;

        eErr =
            papoIndexList[i]->AddEntry( poFeature->GetRawFieldRef( iField ),
                                        poFeature->GetFID() );
    }

    return eErr;
}

/************************************************************************/
/*                          RemoveFromIndex()                           */
/************************************************************************/

OGRErr OGRMILayerAttrIndex::RemoveFromIndex( OGRFeature * /*poFeature*/ )

{
    return OGRERR_UNSUPPORTED_OPERATION;
}

/************************************************************************/
/*                     OGRCreateDefaultLayerIndex()                     */
/************************************************************************/

OGRLayerAttrIndex *OGRCreateDefaultLayerIndex()

{
    return new OGRMILayerAttrIndex();
}

/************************************************************************/
/* ==================================================================== */
/*                            OGRMIAttrIndex                            */
/* ==================================================================== */
/************************************************************************/

/* class declared at top of file */

/************************************************************************/
/*                           OGRMIAttrIndex()                           */
/************************************************************************/

OGRMIAttrIndex::OGRMIAttrIndex( OGRMILayerAttrIndex *poLayerIndex,
                                int iIndexIn, int iFieldIn ) :
    iIndex(iIndexIn),
    poINDFile(poLayerIndex->poINDFile),
    poLIndex(poLayerIndex),
    poFldDefn(poLayerIndex->GetLayer()->GetLayerDefn()->GetFieldDefn(iFieldIn)),
    iField(iFieldIn)
{}

/************************************************************************/
/*                          ~OGRMIAttrIndex()                           */
/************************************************************************/

OGRMIAttrIndex::~OGRMIAttrIndex()
{
}

/************************************************************************/
/*                              AddEntry()                              */
/************************************************************************/

OGRErr OGRMIAttrIndex::AddEntry( OGRField *psKey, GIntBig nFID )

{
    if( psKey == NULL )
        return OGRERR_FAILURE;

    if( nFID >= INT_MAX )
        return OGRERR_FAILURE;

    GByte *pabyKey = BuildKey( psKey );

    if( pabyKey == NULL )
        return OGRERR_FAILURE;

    if( poINDFile->AddEntry( iIndex, pabyKey, (int)nFID+1 ) != 0 )
        return OGRERR_FAILURE;
    else
        return OGRERR_NONE;
}

/************************************************************************/
/*                            RemoveEntry()                             */
/************************************************************************/

OGRErr OGRMIAttrIndex::RemoveEntry( OGRField * /*psKey*/, GIntBig /*nFID*/ )

{
    return OGRERR_UNSUPPORTED_OPERATION;
}

/************************************************************************/
/*                              BuildKey()                              */
/************************************************************************/

GByte *OGRMIAttrIndex::BuildKey( OGRField *psKey )

{
    GByte* ret = NULL;
    switch( poFldDefn->GetType() )
    {
      case OFTInteger:
        ret = poINDFile->BuildKey( iIndex, psKey->Integer );
        break;

      case OFTInteger64:
      {
        if( !CPL_INT64_FITS_ON_INT32(psKey->Integer64) )
        {
            CPLError(CE_Warning, CPLE_NotSupported,
                     "64bit integer value passed to OGRMIAttrIndex::BuildKey()");
        }
        ret = poINDFile->BuildKey( iIndex, (int)psKey->Integer64 );
        break;
      }

      case OFTReal:
        ret = poINDFile->BuildKey( iIndex, psKey->Real );
        break;

      case OFTString:
        ret = poINDFile->BuildKey( iIndex, psKey->String );
        break;

      default:
        CPLAssert( false );
        break;
    }
    return ret;
}

/************************************************************************/
/*                           GetFirstMatch()                            */
/************************************************************************/

GIntBig OGRMIAttrIndex::GetFirstMatch( OGRField *psKey )

{
    GByte *pabyKey = BuildKey( psKey );
    const GIntBig nFID = poINDFile->FindFirst( iIndex, pabyKey );
    if( nFID < 1 )
        return OGRNullFID;
    else
        return nFID - 1;
}

/************************************************************************/
/*                           GetAllMatches()                            */
/************************************************************************/

GIntBig *OGRMIAttrIndex::GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength )
{
    GByte *pabyKey = BuildKey( psKey );

    if (panFIDList == NULL)
    {
        panFIDList = (GIntBig *) CPLMalloc(sizeof(GIntBig) * 2);
        *nFIDCount = 0;
        *nLength = 2;
    }

    GIntBig nFID = poINDFile->FindFirst( iIndex, pabyKey );
    while( nFID > 0 )
    {
        if( *nFIDCount >= *nLength-1 )
        {
            *nLength = (*nLength) * 2 + 10;
            panFIDList = (GIntBig *) CPLRealloc(panFIDList, sizeof(GIntBig)* (*nLength));
        }
        panFIDList[(*nFIDCount)++] = nFID - 1;

        nFID = poINDFile->FindNext( iIndex, pabyKey );
    }

    panFIDList[*nFIDCount] = OGRNullFID;

    return panFIDList;
}

GIntBig *OGRMIAttrIndex::GetAllMatches( OGRField *psKey )
{
    int nFIDCount, nLength;
    return GetAllMatches( psKey, NULL, &nFIDCount, &nLength );
}

/************************************************************************/
/*                               Clear()                                */
/************************************************************************/

OGRErr OGRMIAttrIndex::Clear()

{
    return OGRERR_UNSUPPORTED_OPERATION;
}
