Ticket #326: chunktypes.patch

File chunktypes.patch, 14.7 KB (added by richard, 12 months 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 {