Home Of Steesha

BLOG

C++ std::string分析

2026-04-25
C++ std::string分析

std::string

typedef std::basic_string<char,std::char_traits<char>,std::allocator<char> > std::string;

MSVC 实现

class std::string {
    union {
        char _Buf[16];        // stack-string
        char* _Ptr;           // heap-stirng
    } _Bx;
    unsigned __int64 _Mysize; // size
    unsigned __int64 _Myres;  // capacity
};

SSO

Small String Optimization(SSO),小字符串优化,如果字符串很短(<=15),那么直接存到栈上,如果大于这个阈值,则存堆上。

15字节是因为,字符串需要一个\x00结尾,所以SSO总共16字节。

Assign

直接赋值

代码示例
std::string a;
a = "12345";
/* --Compilation--> */
std::string::_Assign<char>(&a, "12345", 5u);
// ??$_Assign@D@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@AEAAAEAV01@QEBD_K@Z
// std::string *__fastcall std__basic_string_char_std__char_traits_char__std__allocator_char_____Assign_char_(std::string *this, const char *const Ptr, unsigned __int64 Count);
_Assign<char>实现

直接赋值时,传入一个const char* ptr和一个unsigned __int64 Count分别代表要赋值的字符串指针和字符串长度。

然后会先判断String的Capacity是否大于要赋值的字符串长度(也就是 Count <= cap + 1,始终保持有\x00结尾)。

如果Capacity较大,则直接进行memmove,不过需要判断capacity是否<=15,也就是是否为ShortString,然后move到栈/堆上。

如果Capacity较小,意味着需要进行扩容,扩容分下面几个步骤。

  1. 长度检查,需求长度首先对齐到16字节,并要求对齐后的长度nlen不能超过有符号整数范围,避免ptrdiff_t溢出。

  2. 使用贪婪策略计算长度,若nlen小于1.5*cap,则更新长度到1.5*cap,否则nlen不变。

  3. 检测nlen+1是否小于1PageSize,若小于,则直接申请内存,保证了16字节对齐;若大于等于,则多分配40个字节内存,用于32字节对齐,随后对nlen+40进行overflow检查,然后分配nlen+40长度的内存,并使用((alignedNewMalloc + 39) & 0xFFFFFFFFFFFFFFE0uLL)寻找分配内存中的第一个32字节对齐点,并把原始地址埋入这之前8字节的内容。也就是说,多分配的40字节内存中,32字节是为了处理对齐,剩下8字节则编码了malloc返回的未32字节对齐的地址。

  4. 到达endMalloc环节,更新String参数的Capacity和Size,并将新文本拷贝过来,在末尾补0。

  5. 若先前的cap<=0xF,则之前是短字符串,就把堆指针放入开头,由于在栈上,就不需要对之前的字符串进行释放了。

  6. 若先前的cap>0xF,则之前是非短字符串,在堆上,需要对其进行释放,一样的,先看是否是小于一个页(16字节对齐),若是,则直接释放,若非,则是32字节对齐,取到它埋入真实数据前面的8个字节的真实地址,检测完后(防止真实地址被恶意修改到其他位置,导致堆利用),进行释放即可。

  7. 最后,这个函数会返回std::string的指针,方便链式调用。

std::string *__fastcall std::string::_Assign<char>(std::string *this, const char *const Ptr, unsigned __int64 Count)
{
  unsigned __int64 cap; // rbp
  std::string *ssoString; // rdi
  unsigned __int64 nlen; // rdi
  __int64 capHalfLen; // rdx
  char *newMalloc; // r14
  size_t aligned; // rcx
  uint64_t alignedNewMalloc; // rax
  char *ptrOld; // rcx
  unsigned __int64 OldSize; // rdx
  char *ptrRealOld; // rax
​
  cap = this->_Mypair._Myval2._Myres;
  if ( Count > cap )
  {
    nlen = 0x7FFFFFFFFFFFFFFFLL;                // 初始化的值大于 capacity, 需要重新分配
    if ( Count > 0x7FFFFFFFFFFFFFFFLL )
      std::_Xlen_string();                      // string too long
    if ( (Count | 0xF) > 0x7FFFFFFFFFFFFFFFLL || (capHalfLen = cap >> 1, cap > 0x7FFFFFFFFFFFFFFFLL - (cap >> 1)) )
    {                                           // Count | 0xf 计算了十六字节对齐后的大小,还为最后留了一个\x00的位置。
                                                // 后半段在计算1.5倍扩容是否会超出最大大小
      aligned = 0x8000000000000027LL;           // 如果超出2^63,无法分配,设置一个很大的值。
    }
    else
    {
      nlen = Count | 0xF;
      if ( (Count | 0xF) < capHalfLen + cap )   // pad(count) < 1.5 cap
        nlen = capHalfLen + cap;                // 如果1.5cap容量就够了,就申请1.5cap(贪婪策略)
      if ( nlen == 0xFFFFFFFFFFFFFFFFuLL )      // 检测会不会overflow,导致malloc(-1)
      {
        newMalloc = 0;                          // 分配失败
        goto endMalloc;
      }
      if ( nlen + 1 < 0x1000 )                  // 如果申请量刚好等于4KB(1 PageSize), 则不需要经过其他对齐,直接分配即可。
      {
        newMalloc = (char *)operator new(nlen + 1);// <4KB,只保证返回值16字节对齐,不保证32字节对齐。
        goto endMalloc;
      }
      aligned = nlen + 40;                      // 多分配40个字节,为了32字节对齐,还有埋入一个初始地址。
      if ( nlen + 40 < nlen + 1 )               // overflow
        __scrt_throw_std_bad_array_new_length();
    }
    alignedNewMalloc = (uint64_t)operator new(aligned);
    if ( !alignedNewMalloc )
      goto LABEL_24;
    newMalloc = (char *)((alignedNewMalloc + 39) & 0xFFFFFFFFFFFFFFE0uLL);// 从开头开始,寻找32字节对齐的地址(0b1111....1100000)
    *((_QWORD *)newMalloc - 1) = alignedNewMalloc;// 在正式数据之前埋入new返回的地址,用于free。
endMalloc:
    this->_Mypair._Myval2._Mysize = Count;      // 更新string参数
    this->_Mypair._Myval2._Myres = nlen;
    memcpy_0(newMalloc, Ptr, Count);            // 把当前内容拷贝过去
    newMalloc[Count] = 0;                       // 保证最后的是\x00
    if ( cap <= 0xF )                           // 如果旧的string是shortstring
    {
LABEL_27:
      this->_Mypair._Myval2._Bx._Ptr = newMalloc;// 直接覆盖掉前面的值,变成ptr。
      return this;
    }
    ptrOld = this->_Mypair._Myval2._Bx._Ptr;    // 如果旧的string是在堆上的,就要free旧的。
    OldSize = cap + 1;
    if ( cap + 1 < 0x1000 )                     // 在一个页内
    {
      ptrRealOld = this->_Mypair._Myval2._Bx._Ptr;// 直接返回,因为没有32字节对齐。
      goto LABEL_26;
    }
    ptrRealOld = (char *)*((_QWORD *)ptrOld - 1);// 取到真正的初始化地址,因为32字节对齐。
    if ( (unsigned __int64)(ptrOld - ptrRealOld - 8) <= 0x1F )// 防止ptrRealOld被恶意修改,进行检测
    {
      OldSize = cap + 40;
LABEL_26:
      operator delete(ptrRealOld, OldSize);
      goto LABEL_27;
    }
LABEL_24:
    __fastfail(5u);                             // ptrRealOld 损坏
  }
  if ( cap <= 0xF )                             // 如果cap小于等于15,这里也就确认了count<=15
    ssoString = this;
  else
    ssoString = (std::string *)this->_Mypair._Myval2._Bx._Ptr;
  this->_Mypair._Myval2._Mysize = Count;
  memmove_0(ssoString, Ptr, Count);
  ssoString->_Mypair._Myval2._Bx._Buf[Count] = 0;// 直接移动过去即可
  return this;
}

std::cout

对于字符串.c_str()后进行输出,则会判断其是否为短字符,如果不是短字符,则提前解引用一层。

std::cout << a.c_str();
/* --Compliation--> */
p_a = &a;
if ( a._Mypair._Myval2._Myres > 0xF )
    p_a = (std::string *)a._Mypair._Myval2._Bx._Ptr;
std::operator<<<std::char_traits<char>>(std::cout, p_a->_Mypair._Myval2._Bx._Buf);

若是直接输出字符串进行输出,则使用_Insert_string将字符串插入到输出流中,与上面不同的是,_Insert_string传入了字符串长度,而上面则是通过strlen重新获取了字符串长度,且使用_Insert_string可以得益于对String的优化(16/32字节对齐),而进行快速拷贝。

std::cout << a;
/* --Compliation--> */
std::_Insert_string<char,std::char_traits<char>,unsigned __int64>(
    std::cout,
    Ptr->_Mypair._Myval2._Bx._Buf,
    a._Mypair._Myval2._Mysize);
std::ostream::operator<<(std::cout, a._Mypair._Myval2._Mysize);

operator<<<char_traits<char>>

// Hidden C++ exception states: #wind=3
std::ostream *__fastcall std::operator<<<std::char_traits<char>>(std::ostream *Ostr, const char *Val)
{
  std::ostream *v3; // rsi
  unsigned int v4; // ebx
  signed __int64 v5; // rax
  signed __int64 v6; // r15
  __int64 v7; // r9
  __int64 v8; // rdi
  __int64 v9; // rdi
  std::ostream *v10; // r14
  __int64 v11; // rcx
  bool v12; // al
  std::ostream *v13; // rcx
  __int64 v14; // rdx
  __int64 v15; // r8
  __int64 v16; // rcx
  std::ostream *_Ok; // [rsp+20h] [rbp-38h]
  int _State; // [rsp+70h] [rbp+18h]
​
  v3 = Ostr;
  v4 = 0;
  _State = 0;
  v5 = strlen_0(Val);
  v6 = v5;
  v7 = *(int *)(*(_QWORD *)v3->gap0 + 4LL);
  v8 = *(_QWORD *)&v3->gap0[v7 + 40];
  if ( v8 <= 0 || v8 <= v5 )
    v9 = 0;
  else
    v9 = v8 - v5;
  v10 = v3;
  _Ok = v3;
  v11 = *(_QWORD *)&v3->gap0[v7 + 72];
  if ( v11 )
    (*(void (__fastcall **)(__int64))(*(_QWORD *)v11 + 8LL))(v11);
  v12 = std::ios_base::good((std::ios_base *)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL)]);
  if ( v12 )
  {
    v13 = *(std::ostream **)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 80];
    if ( !v13 || v13 == v3 )
    {
      v12 = 1;
    }
    else
    {
      std::ostream::flush();
      v12 = std::ios_base::good((std::ios_base *)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL)]);
    }
  }
  if ( v12 )
  {
    try
    {
      v14 = *(_QWORD *)v3->gap0;
      if ( (*(_DWORD *)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 24] & 0x1C0) != 0x40 )
      {
        while ( v9 > 0 )
        {
          if ( (unsigned int)std::streambuf::sputc(
                               *(_QWORD *)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 72],
                               (unsigned __int8)v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 88]) == -1 )
            goto LABEL_23;
          --v9;
        }
        v14 = *(_QWORD *)v3->gap0;
      }
      if ( std::streambuf::sputn(*(_QWORD *)&v3->gap0[*(int *)(v14 + 4) + 72], Val, v6) == v6 )
      {
        while ( v9 > 0 )
        {
          if ( (unsigned int)std::streambuf::sputc(
                               *(_QWORD *)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 72],
                               (unsigned __int8)v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 88]) == -1 )
            goto LABEL_23;
          --v9;
        }
      }
      else
      {
LABEL_23:
        v4 = 4;
        _State = 4;
      }
      *(_QWORD *)&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL) + 40] = 0;
    }
    catch ( ... )
    {
      LOBYTE(v15) = 1;
      std::ios::setstate(&Ostr->gap0[*(int *)(*(_QWORD *)Ostr->gap0 + 4LL)], 4, v15);
      v3 = Ostr;
      v4 = _State;
      v10 = _Ok;
    }
  }
  else
  {
    v4 = 4;
  }
  std::ios::setstate(&v3->gap0[*(int *)(*(_QWORD *)v3->gap0 + 4LL)], v4, 0);
  if ( !std::uncaught_exceptions() )
    std::ostream::_Osfx(v10);
  v16 = *(_QWORD *)&v10->gap0[*(int *)(*(_QWORD *)v10->gap0 + 4LL) + 72];
  if ( v16 )
    (*(void (__fastcall **)(__int64))(*(_QWORD *)v16 + 16LL))(v16);
  return v3;
}

Size

Size和Length一样,都是直接返回Size而不是进行strlen。

a.size();
/* --Compliation--> */
a._Mypair._Myval2._Mysize;
_NODISCARD _CONSTEXPR20 size_type length() const noexcept {
    return _Mypair._Myval2._Mysize;
}
​
_NODISCARD _CONSTEXPR20 size_type size() const noexcept {
    return _Mypair._Myval2._Mysize;
}

Push_Back

C++的string push_back比较繁琐,首先判断Size是否等于Capacity,这样的话,就需要重新分配。

_CONSTEXPR20 void push_back(const _Elem _Ch) { // insert element at end
    const size_type _Old_size = _Mypair._Myval2._Mysize;
    if (_Old_size < _Mypair._Myval2._Myres) {
        _ASAN_STRING_MODIFY(*this, _Old_size, _Old_size + 1);
        _Mypair._Myval2._Mysize = _Old_size + 1;
        _Elem* const _Ptr       = _Mypair._Myval2._Myptr();
        _Traits::assign(_Ptr[_Old_size], _Ch);
        _Traits::assign(_Ptr[_Old_size + 1], _Elem());
        return;
    }
​
    _Reallocate_grow_by(
        1,
        [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size, const _Elem _Ch)
        _STATIC_LAMBDA {
            _Traits::copy(_New_ptr, _Old_ptr, _Old_size);
            _Traits::assign(_New_ptr[_Old_size], _Ch);
            _Traits::assign(_New_ptr[_Old_size + 1], _Elem());
        },
        _Ch);
}
v11 = a._Mypair._Myval2._Mysize;
if ( a._Mypair._Myval2._Mysize >= a._Mypair._Myval2._Myres )
{
    ____Reallocate_grow_by_V_lambda_1___1__push_back___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__QEAAXD_Z_D___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__AEAAAEAV01__KV_lambda_1___1__push_back_01_QEAAXD_Z_D_Z(
        &a,
        a._Mypair._Myval2._Myres,
        v10,
        '1');
}
else
{
    ++a._Mypair._Myval2._Mysize;
    v12 = &a;
    if ( a._Mypair._Myval2._Myres > 0xF )
        v12 = (std::string *)a._Mypair._Myval2._Bx._Ptr;
    *(_WORD *)&v12->_Mypair._Myval2._Bx._Buf[v11] = '1';
}

然后就是扩容函数,这个和Assign很相似,不同点在于,这个函数默认1.5倍扩大,Count=1,且仅把_Fn一个字符放进最后。

std::string *__fastcall ____Reallocate_grow_by_V_lambda_1___1__push_back___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__QEAAXD_Z_D___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__AEAAAEAV01__KV_lambda_1___1__push_back_01_QEAAXD_Z_D_Z(
    std::string *this,
    const unsigned __int64 <_Args_0>,
    std::string::push_back::__l2::<lambda_1> _Size_increase,
    char _Fn)
{
    size_t Mysize; // r14
    unsigned __int64 v5; // rbx
    unsigned __int64 Myres; // r15
    unsigned __int64 v9; // rcx
    unsigned __int64 v10; // rdx
    _QWORD *v11; // rdi
    unsigned __int64 v12; // rcx
    void *v13; // rax
    char *Ptr; // rbx
    _BYTE *v15; // rcx
​
    Mysize = this->_Mypair._Myval2._Mysize;
    v5 = 0x7FFFFFFFFFFFFFFFLL;
    if ( Mysize == 0x7FFFFFFFFFFFFFFFLL )
        std::_Xlen_string();
    Myres = this->_Mypair._Myval2._Myres;
    v9 = (Mysize + 1) | 0xF;
    if ( v9 > 0x7FFFFFFFFFFFFFFFLL || (v10 = Myres >> 1, Myres > 0x7FFFFFFFFFFFFFFFLL - (Myres >> 1)) )
    {
        v12 = 0x8000000000000027uLL;
    }
    else
    {
        v5 = (Mysize + 1) | 0xF;
        if ( v9 < Myres + v10 )
            v5 = Myres + v10;
        if ( v5 == -1 )
        {
            v11 = 0;
            goto LABEL_15;
        }
        if ( v5 + 1 < 0x1000 )
        {
            v11 = operator new(v5 + 1);
            goto LABEL_15;
        }
        v12 = v5 + 40;
        if ( v5 + 40 < v5 + 1 )
            __scrt_throw_std_bad_array_new_length();
    }
    v13 = operator new(v12);
    if ( !v13 )
        goto LABEL_19;
    v11 = (_QWORD *)(((unsigned __int64)v13 + 39) & 0xFFFFFFFFFFFFFFE0uLL);
    *(v11 - 1) = v13;
    LABEL_15:
    this->_Mypair._Myval2._Mysize = Mysize + 1;
    this->_Mypair._Myval2._Myres = v5;
    if ( Myres <= 0xF )
    {
        memcpy_0(v11, this, Mysize);
        *((_BYTE *)v11 + Mysize) = _Fn;
        *((_BYTE *)v11 + Mysize + 1) = 0;
        goto LABEL_22;
    }
    Ptr = this->_Mypair._Myval2._Bx._Ptr;
    memcpy_0(v11, this->_Mypair._Myval2._Bx._Ptr, Mysize);
    *((_BYTE *)v11 + Mysize) = _Fn;
    *((_BYTE *)v11 + Mysize + 1) = 0;
    if ( Myres + 1 < 0x1000 )
    {
        operator delete(Ptr, Myres + 1);
        goto LABEL_22;
    }
    v15 = (_BYTE *)*((_QWORD *)Ptr - 1);
    if ( (unsigned __int64)(Ptr - v15 - 8) > 0x1F )
        LABEL_19:
    __fastfail(5u);
    operator delete(v15, Myres + 40);
    LABEL_22:
    this->_Mypair._Myval2._Bx._Ptr = (char *)v11;
    return this;
}

Append

Append(const char*)

这种情况,扩容提供两个参数,一个长度一个指针。

Mysize = a._Mypair._Myval2._Mysize;
v7 = &a;
if ( a._Mypair._Myval2._Myres == a._Mypair._Myval2._Mysize )
{
    appended = ____Reallocate_grow_by_V_lambda_1___1_____Append_D___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__AEAAAEAV23_QEBD_K_Z_PEBD_K___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__AEAAAEAV01__KV_lambda_1___1_____Append_D_01_AEAAAEAV01_QEBD0_Z_PEBD_K_Z(
        &a,
        1u,
        (std::string::_Append::__l2::<lambda_1>)a._Mypair._Myval2._Myres,
        v5,
        1u);
}
else
{
    ++a._Mypair._Myval2._Mysize;
    if ( a._Mypair._Myval2._Myres > 0xF )
        v7 = (std::string *)a._Mypair._Myval2._Bx._Ptr;
    strcpy(&v7->_Mypair._Myval2._Bx._Buf[Mysize], "1");
    appended = &a;
}

Sizeincrease和_Fn其实一样,前者更像逻辑上的增量,而后者是给memcpy看的,具体要拷贝过来多少字节。

std::string *__fastcall ____Reallocate_grow_by_V_lambda_1___1_____Append_D___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__AEAAAEAV23_QEBD_K_Z_PEBD_K___basic_string_DU__char_traits_D_std__V__allocator_D_2__std__AEAAAEAV01__KV_lambda_1___1_____Append_D_01_AEAAAEAV01_QEBD0_Z_PEBD_K_Z(
    std::string *this,
    size_t _Size_increase,
    std::string::_Append::__l2::<lambda_1> <_Args_1>,
    const char *a4,
    size_t _Fn)
{
    size_t Mysize; // r15
    unsigned __int64 v6; // rbx
    unsigned __int64 v8; // rbp
    unsigned __int64 Myres; // r14
    unsigned __int64 v10; // rcx
    unsigned __int64 v11; // rdx
    _QWORD *v12; // rdi
    unsigned __int64 v13; // rcx
    void *v14; // rax
    char *v15; // r12
    char *Ptr; // rbx
    _BYTE *v17; // rcx
​
    Mysize = this->_Mypair._Myval2._Mysize;
    v6 = 0x7FFFFFFFFFFFFFFFLL;
    if ( 0x7FFFFFFFFFFFFFFFLL - Mysize < _Size_increase )
        std::_Xlen_string();
    v8 = Mysize + _Size_increase;
    Myres = this->_Mypair._Myval2._Myres;
    v10 = (Mysize + _Size_increase) | 0xF;
    if ( v10 > 0x7FFFFFFFFFFFFFFFLL || (v11 = Myres >> 1, Myres > 0x7FFFFFFFFFFFFFFFLL - (Myres >> 1)) )
    {
        v13 = 0x8000000000000027uLL;
    }
    else
    {
        v6 = v10;
        if ( v10 < Myres + v11 )
            v6 = Myres + v11;
        if ( v6 == -1 )
        {
            v12 = 0;
            goto LABEL_15;
        }
        if ( v6 + 1 < 0x1000 )
        {
            v12 = operator new(v6 + 1);
            goto LABEL_15;
        }
        v13 = v6 + 40;
        if ( v6 + 40 < v6 + 1 )
            __scrt_throw_std_bad_array_new_length();
    }
    v14 = operator new(v13);
    if ( !v14 )
        goto LABEL_19;
    v12 = (_QWORD *)(((unsigned __int64)v14 + 39) & 0xFFFFFFFFFFFFFFE0uLL);
    *(v12 - 1) = v14;
    LABEL_15:
    this->_Mypair._Myval2._Mysize = v8;
    v15 = (char *)v12 + Mysize;
    this->_Mypair._Myval2._Myres = v6;
    if ( Myres <= 0xF )
    {
        memcpy_0(v12, this, Mysize);
        memcpy_0((char *)v12 + Mysize, "1", _Fn);
        v15[_Fn] = 0;
        goto LABEL_22;
    }
    Ptr = this->_Mypair._Myval2._Bx._Ptr;
    memcpy_0(v12, this->_Mypair._Myval2._Bx._Ptr, Mysize);
    memcpy_0((char *)v12 + Mysize, "1", _Fn);
    v15[_Fn] = 0;
    if ( Myres + 1 < 0x1000 )
    {
        operator delete(Ptr, Myres + 1);
        goto LABEL_22;
    }
    v17 = (_BYTE *)*((_QWORD *)Ptr - 1);
    if ( (unsigned __int64)(Ptr - v17 - 8) > 0x1F )
        LABEL_19:
    __fastfail(5u);
    operator delete(v17, Myres + 40);
    LABEL_22:
    this->_Mypair._Myval2._Bx._Ptr = (char *)v12;
    return this;
}

std::wstring

MSVC实现

Assign

std::string不同,std::wstring将Count进行8字节对齐,因为宽字符在Windows下是2字节一个,所以后续处理*2依旧保证其16字节对齐。

std::wstring *__fastcall std::wstring::_Assign<wchar_t>(
        std::wstring *this,
        const wchar_t *const _Ptr,
        unsigned __int64 _Count)
{
  unsigned __int64 Myres; // rbp
  std::wstring *v7; // rsi
  unsigned __int64 v8; // rbx
  unsigned __int64 v10; // rbx
  __int64 v11; // rdx
  unsigned __int64 v12; // rcx
  _QWORD *v13; // rsi
  void *v14; // rax
  wchar_t *Ptr; // rax
  unsigned __int64 v16; // rdx
  void *v17; // rcx
​
  Myres = this->_Mypair._Myval2._Myres;
  if ( _Count > Myres )
  {
    v10 = 0x7FFFFFFFFFFFFFFELL;
    if ( _Count > 0x7FFFFFFFFFFFFFFELL )
      std::_Xlen_string();
    if ( (_Count | 7) > 0x7FFFFFFFFFFFFFFELL || (v11 = Myres >> 1, Myres > 0x7FFFFFFFFFFFFFFELL - (Myres >> 1)) )
    {
      v12 = -2;
    }
    else
    {
      v10 = _Count | 7;
      if ( (_Count | 7) < v11 + Myres )
        v10 = v11 + Myres;
      if ( v10 + 1 > 0x7FFFFFFFFFFFFFFFLL )
        goto LABEL_29;
      v12 = 2 * (v10 + 1);
      if ( !v12 )
      {
        v13 = 0;
        goto LABEL_21;
      }
    }
    if ( v12 < 0x1000 )
    {
      v13 = operator new(v12);
      goto LABEL_21;
    }
    if ( v12 + 39 >= v12 )
    {
      v14 = operator new(v12 + 39);
      if ( !v14 )
        goto LABEL_25;
      v13 = (_QWORD *)(((unsigned __int64)v14 + 39) & 0xFFFFFFFFFFFFFFE0uLL);
      *(v13 - 1) = v14;
LABEL_21:
      this->_Mypair._Myval2._Myres = v10;
      this->_Mypair._Myval2._Mysize = _Count;
      memcpy_0(v13, _Ptr, 2 * _Count);
      *((_WORD *)v13 + _Count) = 0;
      if ( Myres > 7 )
      {
        Ptr = this->_Mypair._Myval2._Bx._Ptr;
        v16 = 2 * Myres + 2;
        if ( v16 >= 0x1000 )
        {
          v17 = (void *)*((_QWORD *)Ptr - 1);
          if ( (unsigned __int64)((char *)Ptr - (_BYTE *)v17 - 8) <= 0x1F )
          {
            operator delete(v17, 2 * Myres + 41);
            this->_Mypair._Myval2._Bx._Ptr = (wchar_t *)v13;
            return this;
          }
LABEL_25:
          __fastfail(5u);
        }
        operator delete(Ptr, v16);
      }
      this->_Mypair._Myval2._Bx._Ptr = (wchar_t *)v13;
      return this;
    }
LABEL_29:
    __scrt_throw_std_bad_array_new_length();
  }
  if ( Myres <= 7 )
    v7 = this;
  else
    v7 = (std::wstring *)this->_Mypair._Myval2._Bx._Ptr;
  v8 = _Count;
  this->_Mypair._Myval2._Mysize = _Count;
  memmove_0(v7, _Ptr, 2 * _Count);
  v7->_Mypair._Myval2._Bx._Buf[v8] = 0;
  return this;
}