Ignore:
Timestamp:
Sep 29, 2008, 6:31:09 PM (17 years ago)
Author:
[email protected]
Message:

2008-09-29 Maciej Stachowiak <[email protected]>

Reviewed by Darin Adler.



It's pretty common in real-world code (and on some of the v8
benchmarks) to append a number to a string, so I made this one of
the fast cases, and also added support to UString to do it
directly without allocating a temporary UString.


~1% speedup on v8 benchmark.

  • VM/Machine.cpp: (JSC::jsAddSlowCase): Make this NEVER_INLINE because somehow otherwise the change is a regression. (JSC::jsAdd): Handle number + string special case. (JSC::Machine::cti_op_add): Integrate much of the logic of jsAdd to avoid exception check in the str + str, num + num and str + num cases.
  • kjs/ustring.cpp: (JSC::expandedSize): Make this a non-member function, since it needs to be called in non-member functions but not outside this file. (JSC::expandCapacity): Ditto. (JSC::UString::expandCapacity): Call the non-member version. (JSC::createRep): Helper to make a rep from a char*. (JSC::UString::UString): Use above helper. (JSC::concatenate): Guts of concatenating constructor for cases where first item is a UString::Rep, and second is a UChar* and length, or a char*. (JSC::UString::append): Implement for cases where first item is a UString::Rep, and second is an int or double. Sadly duplicates logic of UString::from(int) and UString::from(double).
  • kjs/ustring.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/ustring.cpp

    r36263 r37089  
    410410
    411411// put these early so they can be inlined
    412 inline size_t UString::expandedSize(size_t size, size_t otherSize)
     412static inline size_t expandedSize(size_t size, size_t otherSize)
    413413{
    414414    // Do the size calculation in two parts, returning overflowIndicator if
     
    435435}
    436436
    437 void UString::expandCapacity(int requiredLength)
    438 {
    439     m_rep->checkConsistency();
    440 
    441     Rep* r = m_rep->baseString;
     437
     438static inline bool expandCapacity(UString::Rep* rep, int requiredLength)
     439{
     440    rep->checkConsistency();
     441
     442    UString::Rep* r = rep->baseString;
    442443
    443444    if (requiredLength > r->capacity) {
     
    447448        if (!r->buf) {
    448449            r->buf = oldBuf;
    449             makeNull();
    450             return;
     450            return false;
    451451        }
    452452        r->capacity = newCapacity - r->preCapacity;
     
    455455        r->usedCapacity = requiredLength;
    456456
    457     m_rep->checkConsistency();
     457    rep->checkConsistency();
     458    return true;
     459}
     460
     461void UString::expandCapacity(int requiredLength)
     462{
     463    if (!JSC::expandCapacity(m_rep.get(), requiredLength))
     464        makeNull();
    458465}
    459466
     
    485492}
    486493
    487 UString::UString(const char* c)
    488 {
    489     if (!c) {
    490         m_rep = &Rep::null;
    491         return;
    492     }
    493 
    494     if (!c[0]) {
    495         m_rep = &Rep::empty;
    496         return;
    497     }
     494PassRefPtr<UString::Rep> createRep(const char* c)
     495{
     496    if (!c)
     497        return &UString::Rep::null;
     498
     499    if (!c[0])
     500        return &UString::Rep::empty;
    498501
    499502    size_t length = strlen(c);
    500503    UChar* d = allocChars(length);
    501504    if (!d)
    502         makeNull();
     505        return &UString::Rep::null;
    503506    else {
    504507        for (size_t i = 0; i < length; i++)
    505508            d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend
    506         m_rep = Rep::create(d, static_cast<int>(length));
    507     }
     509        return UString::Rep::create(d, static_cast<int>(length));
     510    }
     511
     512}
     513
     514UString::UString(const char* c)
     515    : m_rep(createRep(c))
     516{
    508517}
    509518
     
    532541    else
    533542        m_rep = Rep::createCopying(buffer.data(), buffer.size());
     543}
     544
     545static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const UChar* tData, int tSize)
     546{
     547    RefPtr<UString::Rep> rep = r;
     548
     549    rep->checkConsistency();
     550
     551    int thisSize = rep->size();
     552    int thisOffset = rep->offset;
     553    int length = thisSize + tSize;
     554
     555    // possible cases:
     556    if (tSize == 0) {
     557        // t is empty
     558    } else if (thisSize == 0) {
     559        // this is empty
     560        rep = UString::Rep::createCopying(tData, tSize);
     561    } else if (rep->baseIsSelf() && rep->rc == 1) {
     562        // this is direct and has refcount of 1 (so we can just alter it directly)
     563        if (!expandCapacity(rep.get(), thisOffset + length))
     564            rep = &UString::Rep::null;
     565        if (rep->data()) {
     566            copyChars(rep->data() + thisSize, tData, tSize);
     567            rep->len = length;
     568            rep->_hash = 0;
     569        }
     570    } else if (thisOffset + thisSize == rep->baseString->usedCapacity && thisSize >= minShareSize) {
     571        // this reaches the end of the buffer - extend it if it's long enough to append to
     572        if (!expandCapacity(rep.get(), thisOffset + length))
     573            rep = &UString::Rep::null;
     574        if (rep->data()) {
     575            copyChars(rep->data() + thisSize, tData, tSize);
     576            rep = UString::Rep::create(rep, 0, length);
     577        }
     578    } else {
     579        // this is shared with someone using more capacity, gotta make a whole new string
     580        size_t newCapacity = expandedSize(length, 0);
     581        UChar* d = allocChars(newCapacity);
     582        if (!d)
     583            rep = &UString::Rep::null;
     584        else {
     585            copyChars(d, rep->data(), thisSize);
     586            copyChars(d + thisSize, tData, tSize);
     587            rep = UString::Rep::create(d, length);
     588            rep->capacity = newCapacity;
     589        }
     590    }
     591
     592    rep->checkConsistency();
     593
     594    return rep.release();
     595}
     596
     597static ALWAYS_INLINE PassRefPtr<UString::Rep> concatenate(PassRefPtr<UString::Rep> r, const char* t)
     598{
     599    RefPtr<UString::Rep> rep = r;
     600
     601    rep->checkConsistency();
     602
     603    int thisSize = rep->size();
     604    int thisOffset = rep->offset;
     605    int tSize = static_cast<int>(strlen(t));
     606    int length = thisSize + tSize;
     607
     608    // possible cases:
     609    if (thisSize == 0) {
     610        // this is empty
     611        rep = createRep(t);
     612    } else if (tSize == 0) {
     613        // t is empty, we'll just return *this below.
     614    } else if (rep->baseIsSelf() && rep->rc == 1) {
     615        // this is direct and has refcount of 1 (so we can just alter it directly)
     616        expandCapacity(rep.get(), thisOffset + length);
     617        UChar* d = rep->data();
     618        if (d) {
     619            for (int i = 0; i < tSize; ++i)
     620                d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
     621            rep->len = length;
     622            rep->_hash = 0;
     623        }
     624    } else if (thisOffset + thisSize == rep->baseString->usedCapacity && thisSize >= minShareSize) {
     625        // this string reaches the end of the buffer - extend it
     626        expandCapacity(rep.get(), thisOffset + length);
     627        UChar* d = rep->data();
     628        if (d) {
     629            for (int i = 0; i < tSize; ++i)
     630                d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
     631            rep = UString::Rep::create(rep, 0, length);
     632        }
     633    } else {
     634        // this is shared with someone using more capacity, gotta make a whole new string
     635        size_t newCapacity = expandedSize(length, 0);
     636        UChar* d = allocChars(newCapacity);
     637        if (!d)
     638            rep = &UString::Rep::null;
     639        else {
     640            copyChars(d, rep->data(), thisSize);
     641            for (int i = 0; i < tSize; ++i)
     642                d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
     643            rep = UString::Rep::create(d, length);
     644            rep->capacity = newCapacity;
     645        }
     646    }
     647
     648    rep->checkConsistency();
     649
     650    return rep.release();
    534651}
    535652
     
    600717
    601718    // a does not qualify for append, and b does not qualify for prepend, gotta make a whole new string
    602     size_t newCapacity = UString::expandedSize(length, 0);
     719    size_t newCapacity = expandedSize(length, 0);
    603720    UChar* d = allocChars(newCapacity);
    604721    if (!d)
     
    616733}
    617734
    618 const UString& UString::null()
    619 {
    620     static UString* n = new UString; // Should be called from main thread at least once to be safely initialized.
    621     return *n;
    622 }
    623 
    624 UString UString::from(int i)
     735PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, int i)
    625736{
    626737    UChar buf[1 + sizeof(i) * 3];
     
    633744        char minBuf[1 + sizeof(i) * 3];
    634745        sprintf(minBuf, "%d", INT_MIN);
    635         return UString(minBuf);
     746        return concatenate(rep, minBuf);
    636747    } else {
    637748        bool negative = false;
     
    648759    }
    649760
    650     return UString(p, static_cast<int>(end - p));
    651 }
    652 
    653 UString UString::from(unsigned int u)
    654 {
    655     UChar buf[sizeof(u) * 3];
    656     UChar* end = buf + sizeof(buf) / sizeof(UChar);
    657     UChar* p = end;
    658    
    659     if (u == 0)
    660         *--p = '0';
    661     else {
    662         while (u) {
    663             *--p = static_cast<unsigned short>((u % 10) + '0');
    664             u /= 10;
    665         }
    666     }
    667    
    668     return UString(p, static_cast<int>(end - p));
    669 }
    670 
    671 UString UString::from(long l)
    672 {
    673     UChar buf[1 + sizeof(l) * 3];
    674     UChar* end = buf + sizeof(buf) / sizeof(UChar);
    675     UChar* p = end;
    676 
    677     if (l == 0)
    678         *--p = '0';
    679     else if (l == LONG_MIN) {
    680         char minBuf[1 + sizeof(l) * 3];
    681         sprintf(minBuf, "%ld", LONG_MIN);
    682         return UString(minBuf);
    683     } else {
    684         bool negative = false;
    685         if (l < 0) {
    686             negative = true;
    687             l = -l;
    688         }
    689         while (l) {
    690             *--p = static_cast<unsigned short>((l % 10) + '0');
    691             l /= 10;
    692         }
    693         if (negative)
    694             *--p = '-';
    695     }
    696 
    697     return UString(p, static_cast<int>(end - p));
    698 }
    699 
    700 UString UString::from(double d)
     761    return concatenate(rep, p, static_cast<int>(end - p));
     762
     763}
     764
     765PassRefPtr<UString::Rep> concatenate(UString::Rep* rep, double d)
    701766{
    702767    // avoid ever printing -NaN, in JS conceptually there is only one NaN value
    703768    if (isnan(d))
    704         return "NaN";
     769        return concatenate(rep, "NaN");
    705770
    706771    char buf[80];
     
    761826  freedtoa(result);
    762827
     828  return concatenate(rep, buf);
     829}
     830
     831const UString& UString::null()
     832{
     833    static UString* n = new UString; // Should be called from main thread at least once to be safely initialized.
     834    return *n;
     835}
     836
     837UString UString::from(int i)
     838{
     839    UChar buf[1 + sizeof(i) * 3];
     840    UChar* end = buf + sizeof(buf) / sizeof(UChar);
     841    UChar* p = end;
     842 
     843    if (i == 0)
     844        *--p = '0';
     845    else if (i == INT_MIN) {
     846        char minBuf[1 + sizeof(i) * 3];
     847        sprintf(minBuf, "%d", INT_MIN);
     848        return UString(minBuf);
     849    } else {
     850        bool negative = false;
     851        if (i < 0) {
     852            negative = true;
     853            i = -i;
     854        }
     855        while (i) {
     856            *--p = static_cast<unsigned short>((i % 10) + '0');
     857            i /= 10;
     858        }
     859        if (negative)
     860            *--p = '-';
     861    }
     862
     863    return UString(p, static_cast<int>(end - p));
     864}
     865
     866UString UString::from(unsigned int u)
     867{
     868    UChar buf[sizeof(u) * 3];
     869    UChar* end = buf + sizeof(buf) / sizeof(UChar);
     870    UChar* p = end;
     871   
     872    if (u == 0)
     873        *--p = '0';
     874    else {
     875        while (u) {
     876            *--p = static_cast<unsigned short>((u % 10) + '0');
     877            u /= 10;
     878        }
     879    }
     880   
     881    return UString(p, static_cast<int>(end - p));
     882}
     883
     884UString UString::from(long l)
     885{
     886    UChar buf[1 + sizeof(l) * 3];
     887    UChar* end = buf + sizeof(buf) / sizeof(UChar);
     888    UChar* p = end;
     889
     890    if (l == 0)
     891        *--p = '0';
     892    else if (l == LONG_MIN) {
     893        char minBuf[1 + sizeof(l) * 3];
     894        sprintf(minBuf, "%ld", LONG_MIN);
     895        return UString(minBuf);
     896    } else {
     897        bool negative = false;
     898        if (l < 0) {
     899            negative = true;
     900            l = -l;
     901        }
     902        while (l) {
     903            *--p = static_cast<unsigned short>((l % 10) + '0');
     904            l /= 10;
     905        }
     906        if (negative)
     907            *--p = '-';
     908    }
     909
     910    return UString(p, static_cast<int>(end - p));
     911}
     912
     913UString UString::from(double d)
     914{
     915    // avoid ever printing -NaN, in JS conceptually there is only one NaN value
     916    if (isnan(d))
     917        return "NaN";
     918
     919    char buf[80];
     920    int decimalPoint;
     921    int sign;
     922
     923    char* result = dtoa(d, 0, &decimalPoint, &sign, NULL);
     924    int length = static_cast<int>(strlen(result));
     925 
     926    int i = 0;
     927    if (sign)
     928        buf[i++] = '-';
     929 
     930    if (decimalPoint <= 0 && decimalPoint > -6) {
     931        buf[i++] = '0';
     932        buf[i++] = '.';
     933        for (int j = decimalPoint; j < 0; j++)
     934            buf[i++] = '0';
     935        strcpy(buf + i, result);
     936    } else if (decimalPoint <= 21 && decimalPoint > 0) {
     937        if (length <= decimalPoint) {
     938            strcpy(buf + i, result);
     939            i += length;
     940            for (int j = 0; j < decimalPoint - length; j++)
     941                buf[i++] = '0';
     942            buf[i] = '\0';
     943        } else {
     944            strncpy(buf + i, result, decimalPoint);
     945            i += decimalPoint;
     946            buf[i++] = '.';
     947            strcpy(buf + i, result + decimalPoint);
     948        }
     949    } else if (result[0] < '0' || result[0] > '9')
     950        strcpy(buf + i, result);
     951    else {
     952        buf[i++] = result[0];
     953        if (length > 1) {
     954            buf[i++] = '.';
     955            strcpy(buf + i, result + 1);
     956            i += length - 1;
     957        }
     958       
     959        buf[i++] = 'e';
     960        buf[i++] = (decimalPoint >= 0) ? '+' : '-';
     961        // decimalPoint can't be more than 3 digits decimal given the
     962        // nature of float representation
     963        int exponential = decimalPoint - 1;
     964        if (exponential < 0)
     965            exponential = -exponential;
     966        if (exponential >= 100)
     967            buf[i++] = static_cast<char>('0' + exponential / 100);
     968        if (exponential >= 10)
     969            buf[i++] = static_cast<char>('0' + (exponential % 100) / 10);
     970        buf[i++] = static_cast<char>('0' + exponential % 10);
     971        buf[i++] = '\0';
     972    }
     973   
     974  freedtoa(result);
     975
    763976  return UString(buf);
    764977}
     
    8591072UString& UString::append(const UChar* tData, int tSize)
    8601073{
    861     m_rep->checkConsistency();
    862 
    863     int thisSize = size();
    864     int thisOffset = m_rep->offset;
    865     int length = thisSize + tSize;
    866 
    867     // possible cases:
    868     if (tSize == 0) {
    869         // t is empty
    870     } else if (thisSize == 0) {
    871         // this is empty
    872         m_rep = Rep::createCopying(tData, tSize);
    873     } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
    874         // this is direct and has refcount of 1 (so we can just alter it directly)
    875         expandCapacity(thisOffset + length);
    876         if (data()) {
    877             copyChars(m_rep->data() + thisSize, tData, tSize);
    878             m_rep->len = length;
    879             m_rep->_hash = 0;
    880         }
    881     } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) {
    882         // this reaches the end of the buffer - extend it if it's long enough to append to
    883         expandCapacity(thisOffset + length);
    884         if (data()) {
    885             copyChars(m_rep->data() + thisSize, tData, tSize);
    886             m_rep = Rep::create(m_rep, 0, length);
    887         }
    888     } else {
    889         // this is shared with someone using more capacity, gotta make a whole new string
    890         size_t newCapacity = expandedSize(length, 0);
    891         UChar* d = allocChars(newCapacity);
    892         if (!d)
    893             makeNull();
    894         else {
    895             copyChars(d, data(), thisSize);
    896             copyChars(d + thisSize, tData, tSize);
    897             m_rep = Rep::create(d, length);
    898             m_rep->capacity = newCapacity;
    899         }
    900     }
    901 
    902     m_rep->checkConsistency();
    903 
     1074    m_rep = concatenate(m_rep.release(), tData, tSize);
    9041075    return *this;
    9051076}
     
    9071078UString& UString::append(const char* t)
    9081079{
    909     m_rep->checkConsistency();
    910 
    911     int thisSize = size();
    912     int thisOffset = m_rep->offset;
    913     int tSize = static_cast<int>(strlen(t));
    914     int length = thisSize + tSize;
    915 
    916     // possible cases:
    917     if (thisSize == 0) {
    918         // this is empty
    919         *this = t;
    920     } else if (tSize == 0) {
    921         // t is empty, we'll just return *this below.
    922     } else if (m_rep->baseIsSelf() && m_rep->rc == 1) {
    923         // this is direct and has refcount of 1 (so we can just alter it directly)
    924         expandCapacity(thisOffset + length);
    925         UChar* d = m_rep->data();
    926         if (d) {
    927             for (int i = 0; i < tSize; ++i)
    928                 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
    929             m_rep->len = length;
    930             m_rep->_hash = 0;
    931         }
    932     } else if (thisOffset + thisSize == usedCapacity() && thisSize >= minShareSize) {
    933         // this string reaches the end of the buffer - extend it
    934         expandCapacity(thisOffset + length);
    935         UChar* d = m_rep->data();
    936         if (d) {
    937             for (int i = 0; i < tSize; ++i)
    938                 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
    939             m_rep = Rep::create(m_rep, 0, length);
    940         }
    941     } else {
    942         // this is shared with someone using more capacity, gotta make a whole new string
    943         size_t newCapacity = expandedSize(length, 0);
    944         UChar* d = allocChars(newCapacity);
    945         if (!d)
    946             makeNull();
    947         else {
    948             copyChars(d, data(), thisSize);
    949             for (int i = 0; i < tSize; ++i)
    950                 d[thisSize + i] = static_cast<unsigned char>(t[i]); // use unsigned char to zero-extend instead of sign-extend
    951             m_rep = Rep::create(d, length);
    952             m_rep->capacity = newCapacity;
    953         }
    954     }
    955 
    956     m_rep->checkConsistency();
    957 
     1080    m_rep = concatenate(m_rep.release(), t);
    9581081    return *this;
    9591082}
Note: See TracChangeset for help on using the changeset viewer.