Ticket #326: avoid_string_operations.patch

File avoid_string_operations.patch, 20.0 KB (added by richard, 14 months ago)

Patch to speed search by using a fixed size buffer for keys, rather than C++ strings

  • chert_postlist.h

     
    8383                Xapian::docid did, bool adding, 
    8484                PostlistChunkReader ** from, PostlistChunkWriter **to); 
    8585 
     86#define KEYBUF_MAX_LEN CHERT_BTREE_MAX_KEY_LEN 
     87        mutable char keybuf[KEYBUF_MAX_LEN]; 
     88        mutable size_t keybuf_curlen; 
     89 
    8690        /// Compose a key from a termname and docid. 
    87         static string make_key(const string & term, Xapian::docid did) { 
    88             string key = make_key(term); 
    89             key += pack_uint_preserving_sort(did); 
    90             return key; 
     91        void make_key(const string & term, Xapian::docid did) const { 
     92            keybuf_curlen = 0; 
     93            make_key(term); 
     94            keybuf_curlen += append_packed_uint_preserving_sort( 
     95                keybuf + keybuf_curlen, KEYBUF_MAX_LEN - keybuf_curlen, did); 
    9196        } 
    9297 
    9398        /// Compose a key from a termname. 
    94         static string make_key(const string & term) { 
     99        void make_key(const string & term) const { 
    95100            // Special case for doclen lists. 
    96             if (term.empty()) return string("\x00\xe0", 2); 
     101            if (term.empty()) { 
     102                keybuf[0] = '\x00'; 
     103                keybuf[1] = '\xe0'; 
     104                keybuf_curlen = 2; 
     105                return; 
     106            } 
    97107 
    98             return pack_string_preserving_sort(term); 
     108            keybuf_curlen = append_string_preserving_sort(keybuf, KEYBUF_MAX_LEN, term); 
    99109        } 
    100110 
    101111        bool term_exists(const string & term) const { 
    102             return key_exists(make_key(term)); 
     112            make_key(term); 
     113            return key_exists(keybuf, keybuf_curlen); 
    103114        } 
    104115 
    105116        /** Returns number of docs indexed by @a term. 
     
    129140         */ 
    130141        Xapian::Internal::RefCntPtr<const ChertDatabase> this_db; 
    131142 
     143        ChertPostListTable * pltable; 
     144 
    132145        /// The termname for this postlist. 
    133146        string tname; 
    134147 
  • chert_table.h

     
    227227        memmove(p + I2 + K1, key_.data(), key_len); 
    228228        set_component_of(1); 
    229229    } 
     230    void form_key(const char * key_, size_t keylen) { 
     231        if (keylen > CHERT_BTREE_MAX_KEY_LEN) { 
     232            // We check term length when a term is added to a document but 
     233            // chert doubles zero bytes, so this can still happen for terms 
     234            // which contain one or more zero bytes. 
     235            std::string msg("Key too long: length was "); 
     236            msg += om_tostring(keylen); 
     237            msg += " bytes, maximum length of a key is " 
     238                   STRINGIZE(CHERT_BTREE_MAX_KEY_LEN) " bytes"; 
     239            throw Xapian::InvalidArgumentError(msg); 
     240        } 
     241 
     242        set_key_len(keylen + K1 + C2); 
     243        memmove(p + I2 + K1, key_, keylen); 
     244        set_component_of(1); 
     245    } 
    230246    // FIXME passing cd here is icky 
    231247    void set_tag(int cd, const char *start, int len, bool compressed) { 
    232248        memmove(p + cd, start, len); 
     
    396412         *          false if key is not found in table. 
    397413         */ 
    398414        bool get_exact_entry(const std::string & key, std::string & tag) const; 
     415        bool get_exact_entry(const char * key, size_t keylen, string & tag) const; 
    399416 
    400417        /** Check if a key exists in the Btree. 
    401418         * 
     
    409426         *          false if key is not found in table. 
    410427         */ 
    411428        bool key_exists(const std::string &key) const; 
     429        bool key_exists(const char * key, size_t keylen) const; 
    412430 
    413431        /** Read the tag value for the key pointed to by cursor C_. 
    414432         * 
     
    597615        void read_root(); 
    598616        void split_root(uint4 split_n); 
    599617        void form_key(const std::string & key) const; 
     618        void form_key(const char * key, size_t keylen) const; 
    600619 
    601620        char other_base_letter() const { 
    602621           return (base_letter == 'A') ? 'B' : 'A'; 
  • chert_positionlist.cc

     
    7070              did << ", " << term); 
    7171 
    7272    string data; 
    73     if (!get_exact_entry(pack_uint_preserving_sort(did) + term, data)) { 
     73    string key; 
     74    // 5 bytes is usually enough for a packed uint 
     75    key.reserve(5 + term.size()); 
     76    append_packed_uint_preserving_sort(key, did); 
     77    key.append(term); 
     78    if (!get_exact_entry(key, data)) { 
    7479        // There's no positional information for this term. 
    7580        return 0; 
    7681    } 
     
    106111    positions.clear(); 
    107112 
    108113    string data; 
    109     if (!table->get_exact_entry(pack_uint_preserving_sort(did) + tname, data)) { 
     114    string key; 
     115    // 5 bytes is usually enough for a packed uint 
     116    key.reserve(5 + tname.size()); 
     117    append_packed_uint_preserving_sort(key, did); 
     118    key.append(tname); 
     119    if (!table->get_exact_entry(key, data)) { 
    110120        // There's no positional information for this term. 
    111121        current_pos = positions.begin(); 
    112122        return false; 
  • chert_positionlist.h

     
    3434 
    3535class ChertPositionListTable : public ChertTable { 
    3636    static string make_key(Xapian::docid did, const string & tname) { 
    37         return pack_uint_preserving_sort(did) + tname; 
     37        string result; 
     38        // 5 bytes is usually enough for a packed uint 
     39        result.reserve(5 + tname.size()); 
     40        append_packed_uint_preserving_sort(result, did); 
     41        result.append(tname); 
     42        return result + tname; 
    3843    } 
    3944 
    4045  public: 
  • chert_utils.h

     
    2626#include "omassert.h" 
    2727 
    2828#include <xapian/types.h> 
     29#include <xapian/error.h> 
    2930 
    3031#include <string> 
    3132 
     
    224225 *  of 256 bytes on the length of the integer.  However, this is unlikely to 
    225226 *  ever be a problem. 
    226227 * 
     228 *  @param result A string to append the representation of the integer to. 
    227229 *  @param value  The integer to represent. 
    228  * 
    229  *  @result       A string containing the representation of the integer. 
    230230 */ 
    231231template<class T> 
    232 string 
    233 pack_uint_preserving_sort(T value) 
     232void 
     233append_packed_uint_preserving_sort(string & result, T value) 
    234234{ 
    235235    // Check unsigned 
    236236    STATIC_ASSERT_UNSIGNED_TYPE(T); 
     237    STATIC_ASSERT(sizeof(T) >= 4); 
    237238 
    238     string result; 
     239    string::size_type start = result.size(); 
     240    if (value <= 0xffff) { 
     241        if (value < 0xff) { 
     242            // 1 byte 
     243            char buf[2]; 
     244            buf[0] = char(1); 
     245            buf[1] = char(value & 0xff); 
     246            result.append(buf, 2); 
     247            return; 
     248        } else { 
     249            // 2 bytes 
     250            char buf[3]; 
     251            buf[0] = char(2); 
     252            buf[1] = char((value >> 8) & 0xff); 
     253            buf[2] = char(value & 0xff); 
     254            result.append(buf, 3); 
     255            return; 
     256        } 
     257    } 
     258    if (value <= 0xffffff) { 
     259        // 3 bytes 
     260        char buf[4]; 
     261        buf[0] = char(3); 
     262        buf[1] = char((value >> 16) & 0xff); 
     263        buf[2] = char((value >> 8) & 0xff); 
     264        buf[3] = char(value & 0xff); 
     265        result.append(buf, 4); 
     266        return; 
     267    } 
     268 
     269    if (value <= 0xffffffff) { 
     270        // 4 bytes. 
     271        char buf[5]; 
     272        buf[0] = char(4); 
     273        buf[1] = char((value >> 24) & 0xff); 
     274        buf[2] = char((value >> 16) & 0xff); 
     275        buf[3] = char((value >> 8) & 0xff); 
     276        buf[4] = char(value & 0xff); 
     277        result.append(buf, 5); 
     278        return; 
     279    } 
     280 
     281    if (sizeof(T) > 4) { 
     282        if (result.capacity() < start + 9) { 
     283            result.reserve(start + 9); 
     284        } 
     285        // More than 4 bytes. Do first 4, then loop. 
     286        result.append(string::size_type(1u), char((value >> 24) & 0xff)); 
     287        result.append(string::size_type(1u), char((value >> 16) & 0xff)); 
     288        result.append(string::size_type(1u), char((value >> 8) & 0xff)); 
     289        result.append(string::size_type(1u), char(value & 0xff)); 
     290        value = value >> 16; 
     291        value = value >> 16; 
     292        while (value != 0) { 
     293            om_byte part = static_cast<om_byte>(value & 0xff); 
     294            value = value >> 8; 
     295            result.insert(start, 1u, char(part)); 
     296        } 
     297        result.insert(start, 1u, char(result.size() - start)); 
     298    } 
     299} 
     300 
     301template<class T> 
     302size_t 
     303append_packed_uint_preserving_sort(char * buf, size_t buflen, T value) 
     304{ 
     305    // Check unsigned 
     306    STATIC_ASSERT_UNSIGNED_TYPE(T); 
     307    STATIC_ASSERT(sizeof(T) >= 4); 
     308 
     309    if (value <= 0xffff) { 
     310        if (value < 0xff) { 
     311            if (buflen < 2) 
     312                throw Xapian::InvalidArgumentError("Buffer not long enough to hold 2 byte varint."); 
     313            buf[0] = char(1); 
     314            buf[1] = char(value & 0xff); 
     315            return 2; 
     316        } else { 
     317            if (buflen < 3) 
     318                throw Xapian::InvalidArgumentError("Buffer not long enough to hold 3 byte varint."); 
     319            buf[0] = char(2); 
     320            buf[1] = char((value >> 8) & 0xff); 
     321            buf[2] = char(value & 0xff); 
     322            return 3; 
     323        } 
     324    } 
     325    if (value <= 0xffffff) { 
     326        if (buflen < 4) 
     327            throw Xapian::InvalidArgumentError("Buffer not long enough to hold 4 byte varint."); 
     328        buf[0] = char(3); 
     329        buf[1] = char((value >> 16) & 0xff); 
     330        buf[2] = char((value >> 8) & 0xff); 
     331        buf[3] = char(value & 0xff); 
     332        return 4; 
     333    } 
     334 
     335    if (value <= 0xffffffff) { 
     336        if (buflen < 5) 
     337            throw Xapian::InvalidArgumentError("Buffer not long enough to hold 5 byte varint."); 
     338        buf[0] = char(4); 
     339        buf[1] = char((value >> 24) & 0xff); 
     340        buf[2] = char((value >> 16) & 0xff); 
     341        buf[3] = char((value >> 8) & 0xff); 
     342        buf[4] = char(value & 0xff); 
     343        return 5; 
     344    } 
     345 
     346    // More than 4 bytes. Do first 4, then loop. 
     347    if (buflen < 6) 
     348        throw Xapian::InvalidArgumentError("Buffer not long enough to hold varint."); 
     349    buf[1] = char((value >> 24) & 0xff); 
     350    buf[2] = char((value >> 16) & 0xff); 
     351    buf[3] = char((value >> 8) & 0xff); 
     352    buf[4] = char(value & 0xff); 
     353    value = value >> 16; 
     354    value = value >> 16; 
     355    size_t bytes = 4; 
    239356    while (value != 0) { 
     357        ++bytes; 
     358        if (buflen < bytes + 1) 
     359            throw Xapian::InvalidArgumentError("Buffer not long enough to hold varint."); 
    240360        om_byte part = static_cast<om_byte>(value & 0xff); 
    241361        value = value >> 8; 
    242         result.insert(string::size_type(0), 1u, char(part)); 
     362        buf[bytes] = char(part); 
    243363    } 
    244     result.insert(string::size_type(0), 1u, char(result.size())); 
    245     return result; 
     364    buf[0] = char(bytes); 
    246365} 
    247366 
    248367/** Unpack a unsigned integer, store in sort preserving order. 
     
    343462    return value + '\0'; // Note - next byte mustn't be '\xff'... 
    344463} 
    345464 
     465inline size_t 
     466append_string_preserving_sort(char * buf, size_t buflen, string value) 
     467{ 
     468    string::size_type i, j; 
     469    Assert(buflen >= 2); 
     470    char * pos = buf; 
     471    // bufend is the end of the part of buf which the string goes in. 
     472    const char * bufend = buf + buflen - 2; 
     473    j = value.size(); 
     474    for (i = 0; i != j; ++i) { 
     475        if (pos == bufend) 
     476            throw Xapian::InvalidArgumentError("Buffer not long enough to hold string."); 
     477        *pos = value[i]; 
     478        ++pos; 
     479        if (value[i] == 0) { 
     480            if (pos == bufend) 
     481                throw Xapian::InvalidArgumentError("Buffer not long enough to hold string."); 
     482            *pos = '\xff'; 
     483            ++pos; 
     484        } 
     485    } 
     486    pos[0] = '\0'; 
     487    pos[1] = '\0'; 
     488    return (pos - buf) + 2; 
     489} 
     490 
    346491inline bool 
    347492unpack_string_preserving_sort(const char ** src, 
    348493                              const char * src_end, 
     
    401546inline string 
    402547chert_docid_to_key(Xapian::docid did) 
    403548{ 
    404     return pack_uint_preserving_sort(did); 
     549    string result; 
     550    append_packed_uint_preserving_sort(result, did); 
     551    return result; 
    405552} 
    406553 
    407554#endif /* OM_HGUARD_CHERT_UTILS_H */ 
  • chert_cursor.h

     
    202202         *          otherwise. 
    203203         */ 
    204204        bool find_entry(const string &key); 
     205        bool find_entry(const char * key, size_t keylen) { 
     206            return find_entry(string(key, keylen)); 
     207        } 
    205208 
    206209        /// Position the cursor on the highest entry with key < @a key. 
    207210        void find_entry_lt(const string &key) { 
  • chert_postlist.cc

     
    3434Xapian::doccount 
    3535ChertPostListTable::get_termfreq(const string & term) const 
    3636{ 
    37     string key = make_key(term); 
     37    make_key(term); 
    3838    string tag; 
    39     if (!get_exact_entry(key, tag)) return 0; 
     39    if (!get_exact_entry(keybuf, keybuf_curlen, tag)) return 0; 
    4040 
    4141    Xapian::doccount termfreq; 
    4242    const char * p = tag.data(); 
     
    4747Xapian::termcount 
    4848ChertPostListTable::get_collection_freq(const string & term) const 
    4949{ 
    50     string key = make_key(term); 
     50    make_key(term); 
    5151    string tag; 
    52     if (!get_exact_entry(key, tag)) return 0; 
     52    if (!get_exact_entry(keybuf, keybuf_curlen, tag)) return 0; 
    5353 
    5454    Xapian::termcount collfreq; 
    5555    const char * p = tag.data(); 
     
    9191                            bool is_last_chunk_); 
    9292 
    9393        /// Append an entry to this chunk. 
    94         void append(ChertTable * table, Xapian::docid did, 
     94        void append(ChertPostListTable * table, Xapian::docid did, 
    9595                    Xapian::termcount wdf); 
    9696 
    9797        /// Append a block of raw entries to this chunk. 
     
    110110         *  with a different key to the original one, if for example the first 
    111111         *  entry has changed. 
    112112         */ 
    113         void flush(ChertTable *table); 
     113        void flush(ChertPostListTable *table); 
    114114 
    115115    private: 
    116116        string orig_key; 
     
    314314} 
    315315 
    316316void 
    317 PostlistChunkWriter::append(ChertTable * table, Xapian::docid did, 
     317PostlistChunkWriter::append(ChertPostListTable * table, 
     318                            Xapian::docid did, 
    318319                            Xapian::termcount wdf) 
    319320{ 
    320321    if (!started) { 
     
    331332            is_first_chunk = false; 
    332333            first_did = did; 
    333334            chunk.resize(0); 
    334             orig_key = ChertPostListTable::make_key(tname, first_did); 
     335            table->make_key(tname, first_did); 
     336            orig_key = string(table->keybuf, table->keybuf_curlen); 
    335337        } else { 
    336338            chunk.append(pack_uint(did - current_did - 1)); 
    337339        } 
     
    379381} 
    380382 
    381383void 
    382 PostlistChunkWriter::flush(ChertTable *table) 
     384PostlistChunkWriter::flush(ChertPostListTable *table) 
    383385{ 
    384386    DEBUGCALL(DB, void, "PostlistChunkWriter::flush", table); 
    385387 
     
    560562             * and we just have to write this one back to disk. 
    561563             */ 
    562564            LOGLINE(DB, "PostlistChunkWriter::flush(): rewriting the first chunk, which still has items in it"); 
    563             string key = ChertPostListTable::make_key(tname); 
    564             bool ok = table->get_exact_entry(key, tag); 
     565            table->make_key(tname); 
     566            bool ok = table->get_exact_entry(table->keybuf, table->keybuf_curlen, tag); 
    565567            (void)ok; 
    566568            Assert(ok); 
    567569            Assert(!tag.empty()); 
     
    578580 
    579581            tag += make_start_of_chunk(is_last_chunk, first_did, current_did); 
    580582            tag += chunk; 
    581             table->add(key, tag); 
     583            table->add(string(table->keybuf, table->keybuf_curlen), tag); 
    582584            return; 
    583585        } 
    584586 
     
    609611             * Create a new tag with the correct key, and replace 
    610612             * the old one. 
    611613             */ 
    612             new_key = ChertPostListTable::make_key(tname, first_did); 
     614            table->make_key(tname, first_did); 
     615            new_key = string(table->keybuf, table->keybuf_curlen); 
    613616            table->del(orig_key); 
    614617        } else { 
    615618            new_key = orig_key; 
     
    661664                             const string & tname_, 
    662665                             bool keep_reference) 
    663666        : this_db(keep_reference ? this_db_ : NULL), 
     667          pltable(&(this_db_->postlist_table)), 
    664668          tname(tname_), 
    665669          have_started(false), 
    666670          cursor(this_db_->postlist_table.cursor_get()), 
     
    668672{ 
    669673    DEBUGCALL(DB, void, "ChertPostList::ChertPostList", 
    670674              this_db_.get() << ", " << tname_ << ", " << keep_reference); 
    671     string key = ChertPostListTable::make_key(tname); 
    672     int found = cursor->find_entry(key); 
     675    pltable->make_key(tname); 
     676    int found = cursor->find_entry(pltable->keybuf, pltable->keybuf_curlen); 
    673677    if (!found) { 
    674678        LOGLINE(DB, "postlist for term not found"); 
    675679        number_of_entries = 0; 
     
    823827{ 
    824828    DEBUGCALL(DB, void, 
    825829              "ChertPostList::move_to_chunk_containing", desired_did); 
    826     (void)cursor->find_entry(ChertPostListTable::make_key(tname, desired_did)); 
     830    pltable->make_key(tname, desired_did); 
     831    (void)cursor->find_entry(pltable->keybuf, pltable->keybuf_curlen); 
    827832    Assert(!cursor->after_end()); 
    828833 
    829834    const char * keypos = cursor->current_key.data(); 
     
    965970{ 
    966971    DEBUGCALL(DB, Xapian::docid, "ChertPostListTable::get_chunk", tname << ", " << did << ", " << adding << ", [from], [to]"); 
    967972    // Get chunk containing entry 
    968     string key = make_key(tname, did); 
     973    make_key(tname, did); 
    969974 
    970975    // Find the right chunk 
    971976    AutoPtr<ChertCursor> cursor(cursor_get()); 
    972977 
    973     (void)cursor->find_entry(key); 
     978    (void)cursor->find_entry(keybuf, keybuf_curlen); 
    974979    Assert(!cursor->after_end()); 
    975980 
    976981    const char * keypos = cursor->current_key.data(); 
     
    10501055    LOGVALUE(DB, doclens.size()); 
    10511056    if (!doclens.empty()) { 
    10521057        // Ensure there's a first chunk. 
    1053         string current_key = make_key(string()); 
    1054         if (!key_exists(current_key)) { 
     1058        make_key(string()); 
     1059        if (!key_exists(keybuf, keybuf_curlen)) { 
    10551060            LOGLINE(DB, "Adding dummy first chunk"); 
    10561061            string newtag = make_start_of_first_chunk(0, 0, 0); 
    10571062            newtag += make_start_of_chunk(true, 0, 0); 
    1058             add(current_key, newtag); 
     1063            add(string(keybuf, keybuf_curlen), newtag); 
    10591064        } 
    10601065 
    10611066        map<Xapian::docid, Xapian::termcount>::const_iterator j; 
     
    11161121            map<string, pair<Xapian::termcount_diff, Xapian::termcount_diff> >::const_iterator deltas = freq_deltas.find(tname); 
    11171122            Assert(deltas != freq_deltas.end()); 
    11181123 
    1119             string current_key = make_key(tname); 
     1124            make_key(tname); 
    11201125            string tag; 
    1121             (void)get_exact_entry(current_key, tag); 
     1126            (void)get_exact_entry(keybuf, keybuf_curlen, tag); 
    11221127 
    11231128            // Read start of first chunk to get termfreq and collfreq. 
    11241129            const char *pos = tag.data(); 
     
    11451150                // posting list. 
    11461151                if (islast) { 
    11471152                    // Only one entry for this posting list. 
    1148                     del(current_key); 
     1153                    del(string(keybuf, keybuf_curlen)); 
    11491154                    continue; 
    11501155                } 
    11511156                AutoPtr<ChertCursor> cursor(cursor_get()); 
    1152                 bool found = cursor->find_entry(current_key); 
     1157                bool found = cursor->find_entry(string(keybuf, keybuf_curlen)); 
    11531158                Assert(found); 
    11541159                if (!found) continue; // Reduce damage! 
    11551160                while (cursor->del()) { 
     
    11651170            string newhdr = make_start_of_first_chunk(termfreq, collfreq, firstdid); 
    11661171            newhdr += make_start_of_chunk(islast, firstdid, lastdid); 
    11671172            if (pos == end) { 
    1168                 add(current_key, newhdr); 
     1173                add(string(keybuf, keybuf_curlen), newhdr); 
    11691174            } else { 
    11701175                Assert((size_t)(pos - tag.data()) <= tag.size()); 
    11711176                tag.replace(0, pos - tag.data(), newhdr); 
    1172                 add(current_key, tag); 
     1177                add(string(keybuf, keybuf_curlen), tag); 
    11731178            } 
    11741179        } 
    11751180        map<Xapian::docid, pair<char, Xapian::termcount> >::const_iterator j; 
  • chert_table.cc

     
    10131013    LOGCALL_VOID(DB, "ChertTable::form_key", key); 
    10141014    kt.form_key(key); 
    10151015} 
     1016void ChertTable::form_key(const char * key, size_t keylen) const 
     1017{ 
     1018    LOGCALL_VOID(DB, "ChertTable::form_key", key); 
     1019    kt.form_key(key, keylen); 
     1020} 
    10161021 
    10171022/* ChertTable::add(key, tag) adds the key/tag item to the 
    10181023   B-tree, replacing any existing item with the same key. 
     
    12321237} 
    12331238 
    12341239bool 
     1240ChertTable::get_exact_entry(const char * key, size_t keylen, string & tag) const 
     1241{ 
     1242    LOGCALL(DB, bool, "ChertTable::get_exact_entry", string(key, keylen) << ", [&tag]"); 
     1243    Assert(keylen != 0); 
     1244 
     1245    if (handle < 0) RETURN(false); 
     1246 
     1247    // An oversized key can't exist, so attempting to search for it should fail. 
     1248    if (keylen > CHERT_BTREE_MAX_KEY_LEN) RETURN(false); 
     1249 
     1250    form_key(key, keylen); 
     1251    if (!find(C)) RETURN(false); 
     1252 
     1253    (void)read_tag(C, &tag, false); 
     1254    RETURN(true); 
     1255} 
     1256 
     1257bool 
    12351258ChertTable::key_exists(const string &key) const 
    12361259{ 
    12371260    LOGCALL(DB, bool, "ChertTable::key_exists", key); 
     
    12451268} 
    12461269 
    12471270bool 
     1271ChertTable::key_exists(const char * key, size_t keylen) const 
     1272{ 
     1273    LOGCALL(DB, bool, "ChertTable::key_exists", string(key, keylen)); 
     1274    Assert(keylen != 0); 
     1275 
     1276    // An oversized key can't exist, so attempting to search for it should fail. 
     1277    if (keylen > CHERT_BTREE_MAX_KEY_LEN) RETURN(false); 
     1278 
     1279    form_key(key, keylen); 
     1280    RETURN(find(C)); 
     1281} 
     1282 
     1283bool 
    12481284ChertTable::read_tag(Cursor * C_, string *tag, bool keep_compressed) const 
    12491285{ 
    12501286    LOGCALL(DB, bool, "ChertTable::read_tag", "C_, tag, " << keep_compressed); 
  • chert_values.h

     
    3737{ 
    3838    std::string key("\0\xd8", 2); 
    3939    key += pack_uint(slot); 
    40     key += pack_uint_preserving_sort(did); 
     40    append_packed_uint_preserving_sort(key, did); 
    4141    return key; 
    4242} 
    4343