您当前的位置:首页 > IT编程 > C++
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:C++ BOTAN_ASSERT函数代码示例

51自学网 2021-06-01 19:52:45
  C++
这篇教程C++ BOTAN_ASSERT函数代码示例写得很实用,希望能帮到您。

本文整理汇总了C++中BOTAN_ASSERT函数的典型用法代码示例。如果您正苦于以下问题:C++ BOTAN_ASSERT函数的具体用法?C++ BOTAN_ASSERT怎么用?C++ BOTAN_ASSERT使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。

在下文中一共展示了BOTAN_ASSERT函数的23个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于我们的系统推荐出更棒的C++代码示例。

示例1: if

//staticCPUID::Endian_status CPUID::runtime_check_endian()   {   // Check runtime endian   const uint32_t endian32 = 0x01234567;   const uint8_t* e8 = reinterpret_cast<const uint8_t*>(&endian32);   Endian_status endian = ENDIAN_UNKNOWN;   if(e8[0] == 0x01 && e8[1] == 0x23 && e8[2] == 0x45 && e8[3] == 0x67)      {      endian = ENDIAN_BIG;      }   else if(e8[0] == 0x67 && e8[1] == 0x45 && e8[2] == 0x23 && e8[3] == 0x01)      {      endian = ENDIAN_LITTLE;      }   else      {      throw Internal_Error("Unexpected endian at runtime, neither big nor little");      }   // If we were compiled with a known endian, verify it matches at runtime#if defined(BOTAN_TARGET_CPU_IS_LITTLE_ENDIAN)   BOTAN_ASSERT(endian == ENDIAN_LITTLE, "Build and runtime endian match");#elif defined(BOTAN_TARGET_CPU_IS_BIG_ENDIAN)   BOTAN_ASSERT(endian == ENDIAN_BIG, "Build and runtime endian match");#endif   return endian;   }
开发者ID:webmaster128,项目名称:botan,代码行数:31,


示例2: BOTAN_ASSERT

void Threefish_512::encrypt_n(const byte in[], byte out[], size_t blocks) const   {   BOTAN_ASSERT(m_K.size() == 9, "Key was set");   BOTAN_ASSERT(m_T.size() == 3, "Tweak was set");   for(size_t i = 0; i != blocks; ++i)      {      u64bit X0 = load_le<u64bit>(in, 0);      u64bit X1 = load_le<u64bit>(in, 1);      u64bit X2 = load_le<u64bit>(in, 2);      u64bit X3 = load_le<u64bit>(in, 3);      u64bit X4 = load_le<u64bit>(in, 4);      u64bit X5 = load_le<u64bit>(in, 5);      u64bit X6 = load_le<u64bit>(in, 6);      u64bit X7 = load_le<u64bit>(in, 7);      THREEFISH_INJECT_KEY(0);      THREEFISH_ENC_8_ROUNDS(1,2);      THREEFISH_ENC_8_ROUNDS(3,4);      THREEFISH_ENC_8_ROUNDS(5,6);      THREEFISH_ENC_8_ROUNDS(7,8);      THREEFISH_ENC_8_ROUNDS(9,10);      THREEFISH_ENC_8_ROUNDS(11,12);      THREEFISH_ENC_8_ROUNDS(13,14);      THREEFISH_ENC_8_ROUNDS(15,16);      THREEFISH_ENC_8_ROUNDS(17,18);      store_le(out, X0, X1, X2, X3, X4, X5, X6, X7);      in += 64;      out += 64;      }   }
开发者ID:Andrew-He,项目名称:botan,代码行数:34,


示例3: BOTAN_ASSERT

void EAX_Decryption::finish(secure_vector<byte>& buffer, size_t offset){    BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");    const size_t sz = buffer.size() - offset;    byte* buf = buffer.data() + offset;    BOTAN_ASSERT(sz >= tag_size(), "Have the tag as part of final input");    const size_t remaining = sz - tag_size();    if(remaining)    {        m_cmac->update(buf, remaining);        m_ctr->cipher(buf, buf, remaining);    }    const byte* included_tag = &buf[remaining];    secure_vector<byte> mac = m_cmac->final();    mac ^= m_nonce_mac;    mac ^= m_ad_mac;    if(!same_mem(mac.data(), included_tag, tag_size()))        throw Integrity_Failure("EAX tag check failed");    buffer.resize(offset + remaining);}
开发者ID:Kampbell,项目名称:botan,代码行数:27,


示例4: BOTAN_ASSERT

void Threefish_512::encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const   {   BOTAN_ASSERT(m_K.size() == 9, "Key was set");   BOTAN_ASSERT(m_T.size() == 3, "Tweak was set");#if defined(BOTAN_HAS_THREEFISH_512_AVX2)   if(CPUID::has_avx2())      {      return avx2_encrypt_n(in, out, blocks);      }#endif   BOTAN_PARALLEL_FOR(size_t i = 0; i < blocks; ++i)      {      uint64_t X0, X1, X2, X3, X4, X5, X6, X7;      load_le(in + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);      THREEFISH_INJECT_KEY(0);      THREEFISH_ENC_8_ROUNDS(1,2);      THREEFISH_ENC_8_ROUNDS(3,4);      THREEFISH_ENC_8_ROUNDS(5,6);      THREEFISH_ENC_8_ROUNDS(7,8);      THREEFISH_ENC_8_ROUNDS(9,10);      THREEFISH_ENC_8_ROUNDS(11,12);      THREEFISH_ENC_8_ROUNDS(13,14);      THREEFISH_ENC_8_ROUNDS(15,16);      THREEFISH_ENC_8_ROUNDS(17,18);      store_le(out + BLOCK_SIZE*i, X0, X1, X2, X3, X4, X5, X6, X7);      }   }
开发者ID:lanurmi,项目名称:botan,代码行数:32,


示例5: BOTAN_ASSERT

/** Add a new output queue*/void Output_Buffers::add(SecureQueue* queue)   {   BOTAN_ASSERT(queue, "queue was provided");   BOTAN_ASSERT(m_buffers.size() < m_buffers.max_size(),                "Room was available in container");   m_buffers.push_back(queue);   }
开发者ID:Andrew-He,项目名称:botan,代码行数:12,


示例6: BOTAN_ASSERT

void CCM_Decryption::finish(secure_vector<byte>& buffer, size_t offset)   {   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");   buffer.insert(buffer.begin() + offset, msg_buf().begin(), msg_buf().end());   const size_t sz = buffer.size() - offset;   byte* buf = buffer.data() + offset;   BOTAN_ASSERT(sz >= tag_size(), "We have the tag");   const secure_vector<byte>& ad = ad_buf();   BOTAN_ASSERT(ad.size() % BS == 0, "AD is block size multiple");   const BlockCipher& E = cipher();   secure_vector<byte> T(BS);   E.encrypt(format_b0(sz - tag_size()), T);   for(size_t i = 0; i != ad.size(); i += BS)      {      xor_buf(T.data(), &ad[i], BS);      E.encrypt(T);      }   secure_vector<byte> C = format_c0();   secure_vector<byte> S0(BS);   E.encrypt(C, S0);   inc(C);   secure_vector<byte> X(BS);   const byte* buf_end = &buf[sz - tag_size()];   while(buf != buf_end)      {      const size_t to_proc = std::min<size_t>(BS, buf_end - buf);      E.encrypt(C, X);      xor_buf(buf, X.data(), to_proc);      inc(C);      xor_buf(T.data(), buf, to_proc);      E.encrypt(T);      buf += to_proc;      }   T ^= S0;   if(!same_mem(T.data(), buf_end, tag_size()))      throw Integrity_Failure("CCM tag check failed");   buffer.resize(buffer.size() - tag_size());   }
开发者ID:Andrew-He,项目名称:botan,代码行数:56,


示例7: BOTAN_ASSERT

size_t Sqlite3_Database::Sqlite3_Statement::get_size_t(int column)   {   BOTAN_ASSERT(::sqlite3_column_type(m_stmt, column) == SQLITE_INTEGER,                "Return count is an integer");   const int sessions_int = ::sqlite3_column_int(m_stmt, column);   BOTAN_ASSERT(sessions_int >= 0, "Expected size_t is non-negative");   return static_cast<size_t>(sessions_int);   }
开发者ID:louiz,项目名称:botan,代码行数:11,


示例8: BOTAN_ASSERT

void ECB_Encryption::update(secure_vector<byte>& buffer, size_t offset)   {   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");   const size_t sz = buffer.size() - offset;   byte* buf = buffer.data() + offset;   const size_t BS = cipher().block_size();   BOTAN_ASSERT(sz % BS == 0, "ECB input is full blocks");   const size_t blocks = sz / BS;   cipher().encrypt_n(buf, buf, blocks);   }
开发者ID:AlexNk,项目名称:botan,代码行数:13,


示例9: Invalid_Argument

std::shared_ptr<const X509_Certificate>Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const   {   if(key_hash.size() != 20)      {      throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash");      }   scoped_CFType<CFDataRef> key_hash_cfdata(createCFDataView(key_hash));   check_notnull(key_hash_cfdata, "create key hash search object");   scoped_CFType<CFArrayRef> result(m_impl->search(      {         {kSecAttrPublicKeyHash, key_hash_cfdata.get()},      }));   if(!result)      {      return nullptr;  // no certificate found      }   const auto count = CFArrayGetCount(result.get());   BOTAN_ASSERT(count > 0, "certificate result list contains an object");   // `count` might be greater than 1, but we'll just select the first match   auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), 0));   return readCertificate(cfCert);   }
开发者ID:mgierlings,项目名称:botan,代码行数:28,


示例10: EC_Group

EC_PrivateKey::EC_PrivateKey(const AlgorithmIdentifier& alg_id,                             const secure_vector<byte>& key_bits)   {   m_domain_params = EC_Group(alg_id.parameters);   m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;   OID key_parameters;   secure_vector<byte> public_key_bits;   BER_Decoder(key_bits)      .start_cons(SEQUENCE)         .decode_and_check<size_t>(1, "Unknown version code for ECC key")         .decode_octet_string_bigint(m_private_key)         .decode_optional(key_parameters, ASN1_Tag(0), PRIVATE)         .decode_optional_string(public_key_bits, BIT_STRING, 1, PRIVATE)      .end_cons();   if(!key_parameters.empty() && key_parameters != alg_id.oid)      throw Decoding_Error("EC_PrivateKey - inner and outer OIDs did not match");   if(public_key_bits.empty())      {      m_public_key = domain().get_base_point() * m_private_key;      BOTAN_ASSERT(m_public_key.on_the_curve(),                   "Public point derived from loaded key was on the curve");      }   else      {      m_public_key = OS2ECP(public_key_bits, domain().get_curve());      // OS2ECP verifies that the point is on the curve      }   }
开发者ID:ChrisBFX,项目名称:botan,代码行数:33,


示例11: BOTAN_ASSERT

bool XMSS_WOTS_Verification_Operation::is_valid_signature(const byte sig[],                                                          size_t sig_len)   {   const XMSS_WOTS_Parameters& w = m_pub_key.public_key().wots_parameters();   BOTAN_ASSERT(sig_len == w.element_size() * w.len(),                "Invalid signature size.");   wots_keysig_t signature(0);   signature.reserve(sig_len);   size_t begin = 0;   size_t end = 0;   while(signature.size() < w.len())      {      begin = end;      end = begin + w.element_size();      signature.push_back(secure_vector<byte>(sig + begin, sig + end));      }   XMSS_WOTS_PublicKey pubkey_msg(w.oid(),                                  m_msg_buf,                                  signature,                                  m_pub_key.address(),                                  m_pub_key.public_key().public_seed());   return pubkey_msg.key_data() == m_pub_key.public_key().key_data();   }
开发者ID:louiz,项目名称:botan,代码行数:28,


示例12: BER_Decoder

GOST_3410_PublicKey::GOST_3410_PublicKey(const AlgorithmIdentifier& alg_id,                                         const secure_vector<byte>& key_bits)   {   OID ecc_param_id;   // The parameters also includes hash and cipher OIDs   BER_Decoder(alg_id.parameters).start_cons(SEQUENCE).decode(ecc_param_id);   domain_params = EC_Group(ecc_param_id);   secure_vector<byte> bits;   BER_Decoder(key_bits).decode(bits, OCTET_STRING);   const size_t part_size = bits.size() / 2;   // Keys are stored in little endian format (WTF)   for(size_t i = 0; i != part_size / 2; ++i)      {      std::swap(bits[i], bits[part_size-1-i]);      std::swap(bits[part_size+i], bits[2*part_size-1-i]);      }   BigInt x(bits.data(), part_size);   BigInt y(&bits[part_size], part_size);   public_key = PointGFp(domain().get_curve(), x, y);   BOTAN_ASSERT(public_key.on_the_curve(),                "Loaded GOST 34.10 public key is on the curve");   }
开发者ID:Kampbell,项目名称:botan,代码行数:30,


示例13: cipher

size_t CBC_Decryption::process(uint8_t buf[], size_t sz)   {   const size_t BS = cipher().block_size();   BOTAN_ASSERT(sz % BS == 0, "Input is full blocks");   size_t blocks = sz / BS;   while(blocks)      {      const size_t to_proc = std::min(BS * blocks, m_tempbuf.size());      cipher().decrypt_n(buf, m_tempbuf.data(), to_proc / BS);      xor_buf(m_tempbuf.data(), state_ptr(), BS);      xor_buf(&m_tempbuf[BS], buf, to_proc - BS);      copy_mem(state_ptr(), buf + (to_proc - BS), BS);      copy_mem(buf, m_tempbuf.data(), to_proc);      buf += to_proc;      blocks -= to_proc / BS;      }   return sz;   }
开发者ID:fxdupont,项目名称:botan,代码行数:25,


示例14: result

std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const   {   scoped_CFType<CFArrayRef> result(m_impl->search());   if(!result)      {      return {};  // not a single certificate found in the keychain      }   const auto count = CFArrayGetCount(result.get());   BOTAN_ASSERT(count > 0, "subject result list contains data");   std::vector<X509_DN> output;   output.reserve(count);   for(unsigned int i = 0; i < count; ++i)      {      // Note: Apple's API provides SecCertificateCopyNormalizedSubjectSequence      //       which would have saved us from reading a Botan::X509_Certificate,      //       however, this function applies the same DN "normalization" as      //       stated above.      auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i));      auto cert = readCertificate(cfCert);      output.emplace_back(cert->subject_dn());      }   return output;   }
开发者ID:mgierlings,项目名称:botan,代码行数:27,


示例15: domain

/*** EC_PrivateKey constructor*/EC_PrivateKey::EC_PrivateKey(RandomNumberGenerator& rng,                             const EC_Group& ec_group,                             const BigInt& x,                             bool with_modular_inverse)   {   m_domain_params = ec_group;   if (!ec_group.get_oid().empty())      m_domain_encoding = EC_DOMPAR_ENC_OID;   else      m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;   if(x == 0)      {      m_private_key = BigInt::random_integer(rng, 1, domain().get_order());      }   else      {      m_private_key = x;      }   m_public_key = domain().get_base_point() *                  ((with_modular_inverse) ? inverse_mod(m_private_key, m_domain_params.get_order()) : m_private_key);   BOTAN_ASSERT(m_public_key.on_the_curve(),                "Generated public key point was on the curve");   }
开发者ID:fxdupont,项目名称:botan,代码行数:29,


示例16: BOTAN_ASSERT

void GHASH::update(const uint8_t input[], size_t length)   {   BOTAN_ASSERT(m_ghash.size() == GCM_BS, "Key was set");   m_text_len += length;   ghash_update(m_ghash, input, length);   }
开发者ID:lanurmi,项目名称:botan,代码行数:8,


示例17: BOTAN_ASSERT

void Stream_Decompression::process(secure_vector<byte>& buf, size_t offset, u32bit flags)   {   BOTAN_ASSERT(m_stream, "Initialized");   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");   if(m_buffer.size() < buf.size() + offset)      m_buffer.resize(buf.size() + offset);   m_stream->next_in(buf.data() + offset, buf.size() - offset);   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);   while(true)      {      const bool stream_end = m_stream->run(flags);      if(stream_end)         {         if(m_stream->avail_in() == 0) // all data consumed?            {            m_buffer.resize(m_buffer.size() - m_stream->avail_out());            clear();            break;            }         // More data follows: try to process as a following stream         const size_t read = (buf.size() - offset) - m_stream->avail_in();         start();         m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);         }      if(m_stream->avail_out() == 0)         {         const size_t added = 8 + m_buffer.size();         m_buffer.resize(m_buffer.size() + added);         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);         }      else if(m_stream->avail_in() == 0)         {         m_buffer.resize(m_buffer.size() - m_stream->avail_out());         break;         }      }   copy_mem(m_buffer.data(), buf.data(), offset);   buf.swap(m_buffer);   }
开发者ID:Jesse-V,项目名称:botan,代码行数:46,


示例18: BOTAN_ASSERT

std::vector<byte> Transform_Filter::Nonce_State::get()   {   BOTAN_ASSERT(m_fresh_nonce, "The nonce is fresh for this message");   if(!m_nonce.empty())      m_fresh_nonce = false;   return m_nonce;   }
开发者ID:AlexNk,项目名称:botan,代码行数:8,


示例19: m_cipher1

Cascade_Cipher::Cascade_Cipher(BlockCipher* c1, BlockCipher* c2) :   m_cipher1(c1), m_cipher2(c2)   {   m_block = block_size_for_cascade(c1->block_size(), c2->block_size());   BOTAN_ASSERT(m_block % c1->block_size() == 0 &&                m_block % c2->block_size() == 0,                "Combined block size is a multiple of each ciphers block");   }
开发者ID:binary1248,项目名称:SFNUL,代码行数:9,


示例20: BOTAN_ASSERT

void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)   {   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");   const size_t sz = buffer.size() - offset;   uint8_t* buf = buffer.data() + offset;   BOTAN_ASSERT(sz >= minimum_final_size(), "Have sufficient final input in XTS decrypt");   const size_t BS = cipher().block_size();   if(sz % BS == 0)      {      update(buffer, offset);      }   else      {      // steal ciphertext      const size_t full_blocks = ((sz / BS) - 1) * BS;      const size_t final_bytes = sz - full_blocks;      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);      buffer.resize(full_blocks + offset);      update(buffer, offset);      xor_buf(last, tweak() + BS, BS);      cipher().decrypt(last);      xor_buf(last, tweak() + BS, BS);      for(size_t i = 0; i != final_bytes - BS; ++i)         {         last[i] ^= last[i + BS];         last[i + BS] ^= last[i];         last[i] ^= last[i + BS];         }      xor_buf(last, tweak(), BS);      cipher().decrypt(last);      xor_buf(last, tweak(), BS);      buffer += last;      }   }
开发者ID:mgierlings,项目名称:botan,代码行数:43,


示例21: BOTAN_ASSERT

void CTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)   {   BOTAN_ASSERT(buffer.size() >= offset, "Offset is sane");   const size_t sz = buffer.size() - offset;   uint8_t* buf = buffer.data() + offset;   const size_t BS = cipher().block_size();   if(sz < BS + 1)      throw Encoding_Error(name() + ": insufficient data to decrypt");   if(sz % BS == 0)      {      // swap last two blocks      for(size_t i = 0; i != BS; ++i)         std::swap(buffer[buffer.size()-BS+i], buffer[buffer.size()-2*BS+i]);      update(buffer, offset);      }   else      {      const size_t full_blocks = ((sz / BS) - 1) * BS;      const size_t final_bytes = sz - full_blocks;      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);      buffer.resize(full_blocks + offset);      update(buffer, offset);      cipher().decrypt(last.data());      xor_buf(last.data(), &last[BS], final_bytes - BS);      for(size_t i = 0; i != final_bytes - BS; ++i)         std::swap(last[i], last[i + BS]);      cipher().decrypt(last.data());      xor_buf(last.data(), state_ptr(), BS);      buffer += last;      }   }
开发者ID:fxdupont,项目名称:botan,代码行数:43,


示例22: L

void CCM_Mode::encode_length(size_t len, byte out[])   {   const size_t len_bytes = L();   BOTAN_ASSERT(len_bytes < sizeof(size_t), "Length field fits");   for(size_t i = 0; i != len_bytes; ++i)      out[len_bytes-1-i] = get_byte(sizeof(size_t)-1-i, len);   BOTAN_ASSERT((len >> (len_bytes*8)) == 0, "Message length fits in field");   }
开发者ID:Andrew-He,项目名称:botan,代码行数:11,


示例23: do_decrypt

secure_vector<byte>PK_Decryptor::decrypt_or_random(const byte in[],                                size_t length,                                size_t expected_pt_len,                                RandomNumberGenerator& rng,                                const byte required_content_bytes[],                                const byte required_content_offsets[],                                size_t required_contents_length) const   {   const secure_vector<byte> fake_pms = rng.random_vec(expected_pt_len);   //CT::poison(in, length);   byte valid_mask = 0;   secure_vector<byte> decoded = do_decrypt(valid_mask, in, length);   valid_mask &= CT::is_equal(decoded.size(), expected_pt_len);   decoded.resize(expected_pt_len);   for(size_t i = 0; i != required_contents_length; ++i)      {      /*      These values are chosen by the application and for TLS are constants,      so this early failure via assert is fine since we know 0,1 < 48      If there is a protocol that has content checks on the key where      the expected offsets are controllable by the attacker this could      still leak.      Alternately could always reduce the offset modulo the length?      */      const byte exp = required_content_bytes[i];      const byte off = required_content_offsets[i];      BOTAN_ASSERT(off < expected_pt_len, "Offset in range of plaintext");      valid_mask &= CT::is_equal(decoded[off], exp);      }   CT::conditional_copy_mem(valid_mask,                            /*output*/decoded.data(),                            /*from0*/decoded.data(),                            /*from1*/fake_pms.data(),                            expected_pt_len);   //CT::unpoison(in, length);   //CT::unpoison(decoded.data(), decoded.size());   return decoded;   }
开发者ID:jurajsomorovsky,项目名称:botan,代码行数:52,



注:本文中的BOTAN_ASSERT函数示例整理自Github/MSDocs等源码及文档管理平台,相关代码片段筛选自各路编程大神贡献的开源项目,源码版权归原作者所有,传播和使用请参考对应项目的License;未经允许,请勿转载。


C++ BOXED2RVAL函数代码示例
C++ BOOST_VERIFY函数代码示例
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。