Ticket #326: chunktypes.patch

File chunktypes.patch, 14.7 KB (added by Richard Boulton, 15 years ago)

A patch to add two different chunk types - sparse and compact

  • chert_postlist.h

     
    122122/** A postlist in a chert database.
    123123 */
    124124class ChertPostList : public LeafPostList {
     125    public:
     126        /// Types of chunks.
     127        typedef enum {
     128            /// Items just consist of wdfs - all docids in range are present.
     129            DENSE,
     130
     131            /// Items consist of docid increments and wdfs.
     132            SPARSE
     133        } chunk_type;
     134
    125135    protected: // ChertModifiedPostList needs to access these.
    126136        /** The database we are searching.  This pointer is held so that the
    127137         *  database doesn't get deleted before us, and also to give us access
     
    157167        /// Pointer to byte after end of current chunk.
    158168        const char * end;
    159169
     170        /// The type of the current chunk.
     171        chunk_type current_type;
     172
    160173        /// Document id we're currently at.
    161174        Xapian::docid did;
    162175
     
    179192         *  If already at the end of the chunk, returns false.
    180193         */
    181194        bool next_in_chunk();
     195        bool next_in_chunk_dense();
     196        bool next_in_chunk_sparse();
    182197
    183198        /** Move to the next chunk.
    184199         *
  • chert_postlist.cc

     
    9696
    9797        /// Append a block of raw entries to this chunk.
    9898        void raw_append(Xapian::docid first_did_, Xapian::docid current_did_,
    99                         const string & s) {
     99                        const string & s, ChertPostList::chunk_type type_) {
    100100            Assert(!started);
    101101            first_did = first_did_;
    102102            current_did = current_did_;
    103103            if (!s.empty()) {
    104104                chunk.append(s);
    105105                started = true;
     106                current_type = type_;
    106107            }
    107108        }
    108109
     
    121122
    122123        Xapian::docid first_did;
    123124        Xapian::docid current_did;
     125        ChertPostList::chunk_type current_type;
    124126
    125127        string chunk;
     128        void start_new_chunk(ChertTable * table,
     129                             Xapian::docid new_first_did);
    126130};
    127131
    128132// Static functions
     
    222226read_start_of_chunk(const char ** posptr,
    223227                    const char * end,
    224228                    Xapian::docid first_did_in_chunk,
    225                     bool * is_last_chunk_ptr)
     229                    bool * is_last_chunk_ptr,
     230                    ChertPostList::chunk_type * new_type)
    226231{
    227232    DEBUGCALL_STATIC(DB, Xapian::docid, "read_start_of_chunk",
    228233                     reinterpret_cast<const void*>(posptr) << ", " <<
    229234                     reinterpret_cast<const void*>(end) << ", " <<
    230235                     first_did_in_chunk << ", " <<
    231                      reinterpret_cast<const void*>(is_last_chunk_ptr));
     236                     reinterpret_cast<const void*>(is_last_chunk_ptr) <<
     237                     reinterpret_cast<const void*>(new_type));
    232238
    233239    // Read whether this is the last chunk
    234240    if (!unpack_bool(posptr, end, is_last_chunk_ptr))
     
    241247    if (!unpack_uint(posptr, end, &increase_to_last))
    242248        report_read_error(*posptr);
    243249    Xapian::docid last_did_in_chunk = first_did_in_chunk + increase_to_last;
     250
     251    // Read the chunk format.
     252    unsigned tmp;
     253    if (!unpack_uint(posptr, end, &tmp))
     254        report_read_error(*posptr);
     255    *new_type = ChertPostList::chunk_type(tmp);
     256
    244257    LOGVALUE(DB, last_did_in_chunk);
    245258    RETURN(last_did_in_chunk);
    246259}
     
    260273    Xapian::docid did;
    261274    Xapian::termcount wdf;
    262275
     276    ChertPostList::chunk_type current_type;
     277
    263278  public:
    264279    /** Initialise the postlist chunk reader.
    265280     *
    266281     *  @param first_did  First document id in this chunk.
    267282     *  @param data       The tag string with the header removed.
    268283     */
    269     PostlistChunkReader(Xapian::docid first_did, const string & data_)
    270         : data(data_), pos(data.data()), end(pos + data.length()), at_end(data.empty()), did(first_did)
     284    PostlistChunkReader(Xapian::docid first_did, const string & data_,
     285                        ChertPostList::chunk_type current_type_)
     286        : data(data_), pos(data.data()), end(pos + data.length()),
     287          at_end(data.empty()), did(first_did), current_type(current_type_)
    271288    {
    272289        if (!at_end) read_wdf(&pos, end, &wdf);
    273290    }
     
    294311    if (pos == end) {
    295312        at_end = true;
    296313    } else {
    297         read_did_increase(&pos, end, &did);
     314        switch (current_type) {
     315            case ChertPostList::DENSE:
     316                ++did;
     317                break;
     318            case ChertPostList::SPARSE:
     319                read_did_increase(&pos, end, &did);
     320                break;
     321        }
    298322        read_wdf(&pos, end, &wdf);
    299323    }
    300324}
     
    306330        : orig_key(orig_key_),
    307331          tname(tname_), is_first_chunk(is_first_chunk_),
    308332          is_last_chunk(is_last_chunk_),
    309           started(false)
     333          started(false),
     334          current_type(ChertPostList::DENSE)
    310335{
    311336    DEBUGCALL(DB, void, "PostlistChunkWriter::PostlistChunkWriter",
    312337              orig_key_ << ", " << is_first_chunk_ << ", " << tname_ << ", " <<
     
    314339}
    315340
    316341void
     342PostlistChunkWriter::start_new_chunk(ChertTable * table,
     343                                     Xapian::docid new_first_did)
     344{
     345    bool save_is_last_chunk = is_last_chunk;
     346    is_last_chunk = false;
     347    flush(table);
     348    is_last_chunk = save_is_last_chunk;
     349    is_first_chunk = false;
     350    first_did = new_first_did;
     351    chunk.resize(0);
     352    orig_key = ChertPostListTable::make_key(tname, first_did);
     353    current_type = ChertPostList::DENSE;
     354}
     355
     356void
    317357PostlistChunkWriter::append(ChertTable * table, Xapian::docid did,
    318358                            Xapian::termcount wdf)
    319359{
     
    324364        Assert(did > current_did);
    325365        // Start a new chunk if this one has grown to the threshold.
    326366        if (chunk.size() >= CHUNKSIZE) {
    327             bool save_is_last_chunk = is_last_chunk;
    328             is_last_chunk = false;
    329             flush(table);
    330             is_last_chunk = save_is_last_chunk;
    331             is_first_chunk = false;
    332             first_did = did;
    333             chunk.resize(0);
    334             orig_key = ChertPostListTable::make_key(tname, first_did);
     367            start_new_chunk(table, did);
     368            goto append_packed_wdf;
    335369        } else {
    336             chunk.append(pack_uint(did - current_did - 1));
     370            if (current_type == ChertPostList::DENSE) {
     371                if (current_did + 1 != did) {
     372                    if (current_did == first_did) {
     373                        // Only have one entry - can just change type.
     374                        current_type = ChertPostList::SPARSE;
     375                    } else {
     376                        start_new_chunk(table, did);
     377                        goto append_packed_wdf;
     378                    }
     379                }
     380            }
     381            if (current_type == ChertPostList::SPARSE) {
     382                chunk.append(pack_uint(did - current_did - 1));
     383            }
    337384        }
    338385    }
     386append_packed_wdf:
    339387    current_did = did;
    340388    chunk.append(pack_uint(wdf));
    341389}
     
    355403static inline string
    356404make_start_of_chunk(bool new_is_last_chunk,
    357405                    Xapian::docid new_first_did,
    358                     Xapian::docid new_final_did)
     406                    Xapian::docid new_final_did,
     407                    ChertPostList::chunk_type new_type)
    359408{
    360409    Assert(new_final_did >= new_first_did);
    361410    return pack_bool(new_is_last_chunk) +
    362             pack_uint(new_final_did - new_first_did);
     411            pack_uint(new_final_did - new_first_did) +
     412            pack_uint(static_cast<unsigned>(new_type));
    363413}
    364414
    365415static void
     
    368418                     unsigned int end_of_chunk_header,
    369419                     bool is_last_chunk,
    370420                     Xapian::docid first_did_in_chunk,
    371                      Xapian::docid last_did_in_chunk)
     421                     Xapian::docid last_did_in_chunk,
     422                     ChertPostList::chunk_type new_type)
    372423{
    373424    Assert((size_t)(end_of_chunk_header - start_of_chunk_header) <= chunk.size());
    374425
    375426    chunk.replace(start_of_chunk_header,
    376427                  end_of_chunk_header - start_of_chunk_header,
    377428                  make_start_of_chunk(is_last_chunk, first_did_in_chunk,
    378                                       last_did_in_chunk));
     429                                      last_did_in_chunk, new_type));
    379430}
    380431
    381432void
     
    465516
    466517            // Read the chunk header
    467518            bool new_is_last_chunk;
     519            ChertPostList::chunk_type new_type;
    468520            Xapian::docid new_last_did_in_chunk =
    469521                read_start_of_chunk(&tagpos, tagend, new_first_did,
    470                                     &new_is_last_chunk);
     522                                    &new_is_last_chunk, &new_type);
    471523
    472524            string chunk_data(tagpos, tagend);
    473525
     
    478530            string tag;
    479531            tag = make_start_of_first_chunk(num_ent, coll_freq, new_first_did);
    480532            tag += make_start_of_chunk(new_is_last_chunk,
    481                                               new_first_did,
    482                                               new_last_did_in_chunk);
     533                                       new_first_did,
     534                                       new_last_did_in_chunk,
     535                                       new_type);
    483536            tag += chunk_data;
    484537            table->add(orig_key, tag);
    485538            return;
     
    527580                    report_read_error(keypos);
    528581            }
    529582            bool wrong_is_last_chunk;
     583            ChertPostList::chunk_type new_type;
    530584            string::size_type start_of_chunk_header = tagpos - tag.data();
    531585            Xapian::docid last_did_in_chunk =
    532586                read_start_of_chunk(&tagpos, tagend, first_did_in_chunk,
    533                                     &wrong_is_last_chunk);
     587                                    &wrong_is_last_chunk, &new_type);
    534588            string::size_type end_of_chunk_header = tagpos - tag.data();
    535589
    536590            // write new is_last flag
     
    539593                                 end_of_chunk_header,
    540594                                 true, // is_last_chunk
    541595                                 first_did_in_chunk,
    542                                  last_did_in_chunk);
     596                                 last_did_in_chunk,
     597                                 new_type);
    543598            table->add(cursor->current_key, tag);
    544599        }
    545600    } else {
     
    576631
    577632            tag = make_start_of_first_chunk(num_ent, coll_freq, first_did);
    578633
    579             tag += make_start_of_chunk(is_last_chunk, first_did, current_did);
     634            tag += make_start_of_chunk(is_last_chunk, first_did, current_did, current_type);
    580635            tag += chunk;
    581636            table->add(key, tag);
    582637            return;
     
    616671        }
    617672
    618673        // ...and write the start of this chunk.
    619         tag = make_start_of_chunk(is_last_chunk, first_did, current_did);
     674        tag = make_start_of_chunk(is_last_chunk, first_did, current_did, current_type);
    620675
    621676        tag += chunk;
    622677        table->add(new_key, tag);
     
    687742    did = read_start_of_first_chunk(&pos, end, &number_of_entries, NULL);
    688743    first_did_in_chunk = did;
    689744    last_did_in_chunk = read_start_of_chunk(&pos, end, first_did_in_chunk,
    690                                             &is_last_chunk);
     745                                            &is_last_chunk, &current_type);
    691746    read_wdf(&pos, end, &wdf);
    692747    LOGLINE(DB, "Initial docid " << did);
    693748}
     
    712767    DEBUGCALL(DB, bool, "ChertPostList::next_in_chunk", "");
    713768    if (pos == end) RETURN(false);
    714769
     770    switch (current_type) {
     771        case ChertPostList::DENSE:
     772            ++did;
     773            break;
     774        case ChertPostList::SPARSE:
     775            read_did_increase(&pos, end, &did);
     776            break;
     777    }
     778    read_wdf(&pos, end, &wdf);
     779
     780    // Either not at last doc in chunk, or pos == end, but not both.
     781    Assert(did <= last_did_in_chunk);
     782    Assert(did < last_did_in_chunk || pos == end);
     783    Assert(pos != end || did == last_did_in_chunk);
     784
     785    RETURN(true);
     786}
     787
     788bool
     789ChertPostList::next_in_chunk_dense()
     790{
     791    DEBUGCALL(DB, bool, "ChertPostList::next_in_chunk_dense", "");
     792    if (pos == end) RETURN(false);
     793    Assert(current_type == ChertPostList::DENSE);
     794
     795    ++did;
     796    read_wdf(&pos, end, &wdf);
     797
     798    // Either not at last doc in chunk, or pos == end, but not both.
     799    Assert(did <= last_did_in_chunk);
     800    Assert(did < last_did_in_chunk || pos == end);
     801    Assert(pos != end || did == last_did_in_chunk);
     802
     803    RETURN(true);
     804}
     805
     806bool
     807ChertPostList::next_in_chunk_sparse()
     808{
     809    DEBUGCALL(DB, bool, "ChertPostList::next_in_chunk_sparse", "");
     810    if (pos == end) RETURN(false);
     811    Assert(current_type == ChertPostList::SPARSE);
     812
    715813    read_did_increase(&pos, end, &did);
    716814    read_wdf(&pos, end, &wdf);
    717815
     
    765863
    766864    first_did_in_chunk = did;
    767865    last_did_in_chunk = read_start_of_chunk(&pos, end, first_did_in_chunk,
    768                                             &is_last_chunk);
     866                                            &is_last_chunk, &current_type);
    769867    read_wdf(&pos, end, &wdf);
    770868}
    771869
     
    859957
    860958    first_did_in_chunk = did;
    861959    last_did_in_chunk = read_start_of_chunk(&pos, end, first_did_in_chunk,
    862                                             &is_last_chunk);
     960                                            &is_last_chunk, &current_type);
    863961    read_wdf(&pos, end, &wdf);
    864962
    865963    // Possible, since desired_did might be after end of this chunk and before
     
    872970{
    873971    DEBUGCALL(DB, bool,
    874972              "ChertPostList::move_forward_in_chunk_to_at_least", desired_did);
     973    Assert(did <= desired_did);
    875974    if (desired_did > last_did_in_chunk) {
    876975        pos = end;
    877976        RETURN(false);
    878977    }
    879     while (did < desired_did) {
    880         bool at_end_of_chunk = !next_in_chunk();
    881         // If we hit the end of the chunk then last_did_in_chunk must be
    882         // wrong.
    883         Assert(!at_end_of_chunk);
    884         if (at_end_of_chunk) RETURN(false);
     978    switch (current_type) {
     979        case ChertPostList::DENSE: {
     980            unsigned count = desired_did - did;
     981            if (count) {
     982                if (count > 1) {
     983                    count -= 1;
     984                    while (pos != end) {
     985                        om_byte part = static_cast<om_byte>(*pos);
     986                        ++pos;
     987                        if ((part & 0x80) == 0) {
     988                            if ((--count) == 0)
     989                                break;
     990                        }
     991                    }
     992                    if (pos == end)
     993                        throw Xapian::DatabaseCorruptError("Insufficient entries in posting list chunk");
     994                }
     995                read_wdf(&pos, end, &wdf);
     996                did = desired_did;
     997            }
     998            break;
     999        }
     1000        case ChertPostList::SPARSE:
     1001            while (did < desired_did) {
     1002                bool at_end_of_chunk = !next_in_chunk_sparse();
     1003                // If we hit the end of the chunk then last_did_in_chunk must be
     1004                // wrong.
     1005                Assert(!at_end_of_chunk);
     1006                if (at_end_of_chunk) RETURN(false);
     1007            }
     1008            break;
    8851009    }
    8861010    RETURN(true);
    8871011}
     
    10041128    }
    10051129
    10061130    bool is_last_chunk;
     1131    ChertPostList::chunk_type current_type;
    10071132    Xapian::docid last_did_in_chunk;
    1008     last_did_in_chunk = read_start_of_chunk(&pos, end, first_did_in_chunk, &is_last_chunk);
     1133    last_did_in_chunk = read_start_of_chunk(&pos, end, first_did_in_chunk, &is_last_chunk, &current_type);
    10091134    *to = new PostlistChunkWriter(cursor->current_key, is_first_chunk, tname,
    10101135                                  is_last_chunk);
    10111136    if (did > last_did_in_chunk) {
     
    10141139        // (FIXME)
    10151140        *from = NULL;
    10161141        (*to)->raw_append(first_did_in_chunk, last_did_in_chunk,
    1017                           string(pos, end));
     1142                          string(pos, end), current_type);
    10181143    } else {
    1019         *from = new PostlistChunkReader(first_did_in_chunk, string(pos, end));
     1144        *from = new PostlistChunkReader(first_did_in_chunk, string(pos, end),
     1145                                        current_type);
    10201146    }
    10211147    if (is_last_chunk) RETURN(Xapian::docid(-1));
    10221148
     
    10541180        if (!key_exists(current_key)) {
    10551181            LOGLINE(DB, "Adding dummy first chunk");
    10561182            string newtag = make_start_of_first_chunk(0, 0, 0);
    1057             newtag += make_start_of_chunk(true, 0, 0);
     1183            newtag += make_start_of_chunk(true, 0, 0, ChertPostList::DENSE);
    10581184            add(current_key, newtag);
    10591185        }
    10601186
     
    11261252            Xapian::termcount termfreq, collfreq;
    11271253            Xapian::docid firstdid, lastdid;
    11281254            bool islast;
     1255            ChertPostList::chunk_type current_type;
    11291256            if (pos == end) {
    11301257                termfreq = 0;
    11311258                collfreq = 0;
     
    11361263                firstdid = read_start_of_first_chunk(&pos, end,
    11371264                                                     &termfreq, &collfreq);
    11381265                // Handle the generic start of chunk header.
    1139                 lastdid = read_start_of_chunk(&pos, end, firstdid, &islast);
     1266                lastdid = read_start_of_chunk(&pos, end, firstdid, &islast, &current_type);
    11401267            }
    11411268
    11421269            termfreq += deltas->second.first;
     
    11631290
    11641291            // Rewrite start of first chunk to update termfreq and collfreq.
    11651292            string newhdr = make_start_of_first_chunk(termfreq, collfreq, firstdid);
    1166             newhdr += make_start_of_chunk(islast, firstdid, lastdid);
     1293            newhdr += make_start_of_chunk(islast, firstdid, lastdid, current_type);
    11671294            if (pos == end) {
    11681295                add(current_key, newhdr);
    11691296            } else {