chenjunfu2-nbt-cpp v2.1.0
一个基于CPP20的NBT(Named Binary Tag)库
载入中...
搜索中...
未找到
NBT_IO.hpp
浏览该文件的文档.
1#pragma once
2
3#include <stdio.h>
4#include <stdint.h>
5#include <stddef.h>//size_t
6#include <string.h>//memcpy
7#include <fstream>
8#include <vector>
9#include <filesystem>
10
11#include "NBT_Print.hpp"//打印输出
12
13#include "vcpkg_config.h"//包含vcpkg生成的配置以确认库安装情况
14
15#ifdef CJF2_NBT_CPP_USE_ZLIB
16
17/*zlib*/
18#define ZLIB_CONST
19#include <zlib.h>
20/*zlib*/
21
22#endif
23
26
28class NBT_IO
29{
31 NBT_IO(void) = delete;
33 ~NBT_IO(void) = delete;
34
35public:
43 template <typename T = std::vector<uint8_t>>
45 {
46 private:
47 const T &tData = {};
48 size_t szIndex = 0;
49
50 public:
52 using StreamType = T;
54 using ValueType = typename T::value_type;
55
56 //静态断言确保字节流与可平凡拷贝
57 static_assert(sizeof(ValueType) == 1, "Error ValueType Size");
58 static_assert(std::is_trivially_copyable_v<ValueType>, "ValueType Must Be Trivially Copyable");
59
61 DefaultInputStream(const T &&_tData, size_t szStartIdx = 0) = delete;
62
67 DefaultInputStream(const T &_tData, size_t szStartIdx = 0) :tData(_tData), szIndex(szStartIdx)
68 {}
69
71 ~DefaultInputStream(void) = default;
80
85 const ValueType &operator[](size_t szIndex) const noexcept
86 {
87 return tData[szIndex];
88 }
89
93 const ValueType &GetNext() noexcept
94 {
95 return tData[szIndex++];
96 }
97
102 void GetRange(void *pDest, size_t szSize) noexcept
103 {
104 memcpy(pDest, &tData[szIndex], szSize);
105 szIndex += szSize;
106 }
107
110 void UnGet() noexcept
111 {
112 if (szIndex != 0)
113 {
114 --szIndex;
115 }
116 }
117
121 const ValueType *CurData() const noexcept
122 {
123 return &(tData[szIndex]);
124 }
125
130 size_t AddIndex(size_t szSize) noexcept
131 {
132 return szIndex += szSize;
133 }
134
139 size_t SubIndex(size_t szSize) noexcept
140 {
141 return szIndex -= szSize;
142 }
143
146 bool IsEnd() const noexcept
147 {
148 return szIndex >= tData.size();
149 }
150
153 size_t Size() const noexcept
154 {
155 return tData.size();
156 }
157
161 bool HasAvailData(size_t szSize) const noexcept
162 {
163 return (tData.size() - szIndex) >= szSize;
164 }
165
167 void Reset() noexcept
168 {
169 szIndex = 0;
170 }
171
174 const ValueType *BaseData() const noexcept
175 {
176 return tData.data();
177 }
178
181 size_t Index() const noexcept
182 {
183 return szIndex;
184 }
185
189 size_t &Index() noexcept
190 {
191 return szIndex;
192 }
193 };
194
202 template <typename T = std::vector<uint8_t>>
204 {
205 private:
206 T &tData;
207
208 public:
210 using StreamType = T;
212 using ValueType = typename T::value_type;
213
214 //静态断言确保字节流与可平凡拷贝
215 static_assert(sizeof(ValueType) == 1, "Error ValueType Size");
216 static_assert(std::is_trivially_copyable_v<ValueType>, "ValueType Must Be Trivially Copyable");
217
222 DefaultOutputStream(T &_tData, size_t szStartIdx = 0) :tData(_tData)//引用天生无法使用临时值构造,无需担心临时值构造导致的悬空引用
223 {
224 tData.resize(szStartIdx);
225 }
226
228 ~DefaultOutputStream(void) = default;
237
242 const ValueType &operator[](size_t szIndex) const noexcept
243 {
244 return tData[szIndex];
245 }
246
251 template<typename V>
252 requires(std::is_constructible_v<ValueType, V &&>)
253 void PutOnce(V &&c)
254 {
255 tData.push_back(std::forward<V>(c));
256 }
257
262 void PutRange(const ValueType *pData, size_t szSize)
263 {
264 //tData.insert(tData.end(), &pData[0], &pData[szSize]);
265
266 //使用更高效的实现,而不是迭代器写入
267 size_t szCurSize = tData.size();
268 tData.resize(szCurSize + szSize);
269 memcpy(&tData.data()[szCurSize], &pData[0], szSize);
270 }
271
276 void AddReserve(size_t szAddSize)
277 {
278 tData.reserve(tData.size() + szAddSize);
279 }
280
282 void UnPut(void) noexcept
283 {
284 tData.pop_back();
285 }
286
289 size_t Size(void) const noexcept
290 {
291 return tData.size();
292 }
293
295 void Reset(void) noexcept
296 {
297 tData.clear();
298 }
299
302 const T &Data(void) const noexcept
303 {
304 return tData;
305 }
306
309 T &Data(void) noexcept
310 {
311 return tData;
312 }
313 };
314
315
316public:
324 template<typename T = std::vector<uint8_t>>
325 requires (sizeof(typename T::value_type) == 1 && std::is_trivially_copyable_v<typename T::value_type>)
326 static bool WriteFile(const std::filesystem::path &pathFileName, const T &tData)
327 {
328 std::fstream fWrite;
329 fWrite.open(pathFileName, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
330 if (!fWrite)
331 {
332 return false;
333 }
334
335 //获取文件大小并写出
336 uint64_t qwFileSize = tData.size();
337 if (!fWrite.write((const char *)tData.data(), sizeof(tData[0]) * qwFileSize))
338 {
339 return false;
340 }
341
342 //完成,关闭文件
343 fWrite.close();
344
345 return true;
346 }
347
355 template<typename T = std::vector<uint8_t>>
356 requires (sizeof(typename T::value_type) == 1 && std::is_trivially_copyable_v<typename T::value_type>)
357 static bool ReadFile(const std::filesystem::path &pathFileName, T &tData)
358 {
359 std::fstream fRead;
360 fRead.open(pathFileName, std::ios_base::binary | std::ios_base::in);
361 if (!fRead)
362 {
363 return false;
364 }
365
366 //获取文件大小
367 // 移动到文件末尾
368 if (!fRead.seekg(0, std::ios::end))
369 {
370 return false;
371 }
372
373 // 获取文件大小
374 size_t szFileSize = fRead.tellg();
375
376 //回到文件开头
377 if (!fRead.seekg(0, std::ios::beg))
378 {
379 return false;
380 }
381
382 //直接给数据塞string里
383 tData.resize(szFileSize);//设置长度 c++23用resize_and_overwrite
384 if (!fRead.read((char *)tData.data(), sizeof(tData[0]) * szFileSize))//直接读入data
385 {
386 return false;
387 }
388
389 //完成,关闭文件
390 fRead.close();
391
392 return true;
393 }
394
400 static bool IsFileExist(const std::filesystem::path &pathFileName)
401 {
402 std::error_code ec;//判断这东西是不是true确定有没有error
403 bool bExists = std::filesystem::exists(pathFileName, ec);
404
405 return !ec && bExists;//没有错误并且存在
406 }
407
408#ifdef CJF2_NBT_CPP_USE_ZLIB
409
415 static bool IsZlib(uint8_t u8DataFirst, uint8_t u8DataSecond)
416 {
417 return u8DataFirst == (uint8_t)0x78 &&
418 (
419 u8DataSecond == (uint8_t)0x9C ||
420 u8DataSecond == (uint8_t)0x01 ||
421 u8DataSecond == (uint8_t)0xDA ||
422 u8DataSecond == (uint8_t)0x5E
423 );
424 }
425
431 static bool IsGzip(uint8_t u8DataFirst, uint8_t u8DataSecond)
432 {
433 return u8DataFirst == (uint8_t)0x1F && u8DataSecond == (uint8_t)0x8B;
434 }
435
442 template<typename T>
443 requires (sizeof(typename T::value_type) == 1 && std::is_trivially_copyable_v<typename T::value_type>)
444 static bool IsDataZipped(const T &tData)
445 {
446 if (tData.size() <= 2)
447 {
448 return false;
449 }
450
451 uint8_t u8DataFirst = (uint8_t)tData[0];
452 uint8_t u8DataSecond = (uint8_t)tData[1];
453
454 return IsZlib(u8DataFirst, u8DataSecond) || IsGzip(u8DataFirst, u8DataSecond);
455 }
456
464 template<typename I, typename O>
465 requires (sizeof(typename I::value_type) == 1 && std::is_trivially_copyable_v<typename I::value_type> &&
466 sizeof(typename O::value_type) == 1 && std::is_trivially_copyable_v<typename O::value_type>)
467 static void DecompressData(O &oData, const I &iData)
468 {
469 if (std::addressof(oData) == std::addressof(iData))
470 {
471 throw std::runtime_error("The oData object cannot be the iData object");
472 }
473
474 if (iData.empty())
475 {
476 oData.clear();
477 return;
478 }
479
480 z_stream zs
481 {
482 .next_in = Z_NULL,
483 .avail_in = 0,
484 .total_in = 0,
485
486 .next_out = Z_NULL,
487 .avail_out = 0,
488 .total_out = 0,
489
490 .msg = Z_NULL,
491 .state = Z_NULL,
492
493 .zalloc = (alloc_func)Z_NULL,
494 .zfree = (free_func)Z_NULL,
495 .opaque = (voidpf)Z_NULL,
496
497 .data_type = Z_BINARY,
498
499 .adler = 0,
500 .reserved = {},
501 };
502
503 if (inflateInit2(&zs, 32 + 15) != Z_OK)//32+15自动判断是gzip还是zlib
504 {
505 throw std::runtime_error("Failed to initialize zlib decompression");
506 }
507
508 //对于大于uint_max的数据来说,切分为多个uint_max的块作为流依次输入
509 //注意zlib在实现内会移动指针,所以对于大于一定字节的情况下,只需要更新
510 //avail_in,而无须更新next_in。这里的容器保证不变,所以地址不会失效
511 zs.next_in = (z_const Bytef *)iData.data();
512
513 //默认解压大小为压缩大小,后续扩容
514 oData.resize(iData.size());
515
516 //两个变量用于记录已经压缩的大小和剩余数据大小
517 size_t szDecompressedSize = 0;
518 size_t szRemainingSize = iData.size();
519 int iRet = Z_OK;
520 do
521 {
522 //首先计算剩余可用空间,然后决定是否扩容
523 size_t szOut = oData.size() - szDecompressedSize;
524 if (szOut == 0)
525 {
526 oData.resize(oData.size() * 2);
527 szOut = oData.size() - szDecompressedSize;
528 }
529
530 //虽然next_out也会在内部实现被移动,但是oData是容器,会被扩容
531 //一旦扩容触发,那么实际上的地址就可能会改变,所以必须每次重新获取
532 //切记上面与这里的代码顺序不可改变,必须在上面计算并扩容完成后获取地址
533 zs.next_out = (Bytef *)(&oData.data()[szDecompressedSize]);
534
535 //如果输入被消耗完,重新赋值
536 if (zs.avail_in == 0)
537 {
538 constexpr uInt uIntMax = (uInt)-1;
539 zs.avail_in = szRemainingSize > (size_t)uIntMax ? uIntMax : (uInt)szRemainingSize;
540 szRemainingSize -= zs.avail_in;//缩小剩余待处理大小
541 }
542
543 //如果输出大小耗尽,重新赋值
544 if (zs.avail_out == 0)
545 {
546 constexpr uInt uIntMax = (uInt)-1;
547 zs.avail_out = szOut > (size_t)uIntMax ? uIntMax : (uInt)szOut;
548 //这里不对szOut处理,因为会在开头与结尾计算
549 }
550
551 //解压,如果剩余不为0则代表分块了,使用Z_NO_FLUSH,否则Z_FINISH
552 iRet = inflate(&zs, szRemainingSize != 0 ? Z_NO_FLUSH : Z_FINISH);
553
554 //计算本次解压的大小
555 szDecompressedSize += szOut - zs.avail_out;
556 } while (iRet == Z_OK || iRet == Z_BUF_ERROR);//只要没错误(缓冲区不够大除外)或者没到结尾就继续运行
557
558 inflateEnd(&zs);//结束解压
559 oData.resize(szDecompressedSize);//设置解压大小
560
561 //错误处理
562 if (iRet != Z_STREAM_END)
563 {
564 if (zs.msg != NULL)//错误消息不为null则抛出带消息异常
565 {
566 throw std::runtime_error(std::string("Zlib decompression failed with error message: ") + std::string(zs.msg));
567 }
568 else//如果为null直接使用错误码
569 {
570 throw std::runtime_error(std::string("Zlib decompression failed with error code: ") + std::to_string(iRet));
571 }
572 }
573 }
574
583 template<typename I, typename O>
584 requires (sizeof(typename I::value_type) == 1 && std::is_trivially_copyable_v<typename I::value_type> &&
585 sizeof(typename O::value_type) == 1 && std::is_trivially_copyable_v<typename O::value_type>)
586 static void CompressData(O &oData, const I &iData, int iLevel = Z_DEFAULT_COMPRESSION)
587 {
588 if (std::addressof(oData) == std::addressof(iData))
589 {
590 throw std::runtime_error("The oData object cannot be the iData object");
591 }
592
593 if (iData.empty())
594 {
595 oData.clear();
596 return;
597 }
598
599 z_stream zs
600 {
601 .next_in = Z_NULL,
602 .avail_in = 0,
603 .total_in = 0,
604
605 .next_out = Z_NULL,
606 .avail_out = 0,
607 .total_out = 0,
608
609 .msg = Z_NULL,
610 .state = Z_NULL,
611
612 .zalloc = (alloc_func)Z_NULL,
613 .zfree = (free_func)Z_NULL,
614 .opaque = (voidpf)Z_NULL,
615
616 .data_type = Z_BINARY,
617
618 .adler = 0,
619 .reserved = {},
620 };
621
622 if (deflateInit2(&zs, iLevel, Z_DEFLATED, 16 + 15, 8, Z_DEFAULT_STRATEGY) != Z_OK)//16+15使用gzip(mojang默认格式)
623 {
624 throw std::runtime_error("Failed to initialize zlib compression");
625 }
626
627 //与上方解压例程相同,不做重复解释
628 zs.next_in = (z_const Bytef *)iData.data();
629
630 /*
631 如果范围溢出,则设置为比原始数据的大小加12字节大0.1%的一半
632 这样做的目的是为了尽可能缩小一开始的体积
633 比如实际上压缩率非常低的情况下可能根本用不到一半
634 但是如果实际上压缩率很高,那么下面二倍扩容一次后刚好就是最坏情况
635 这样基本上性能较优,下面zlib文档说明的最坏情况:
636
637 Upon entry, destLen is the total size of the
638 destination buffer, which must be at least 0.1%
639 larger than sourceLen plus 12 bytes.
640 */
641
642 constexpr uLong uLongMax = (uLong)-1;
643 if (iData.size() > (size_t)uLongMax)
644 {
645 size_t szNeedSize = iData.size() + 12;//先比原始数据大12byte
646 //注意这里使用了向上取整的整数除法
647 //加等于自身的0.1%相当于比原先的自己大0.1%,这里的1/1000就是0.1/100
648 szNeedSize += (szNeedSize + (1000 - 1)) / 1000;
649 //设置目标大小
650 oData.resize((szNeedSize + (2 - 1)) / 2);//向上取整除以二
651 }
652 else
653 {
654 //在范围未溢出的情况下,进行预测
655 //把压缩大小设置为预测的压缩大小
656 oData.resize(deflateBound(&zs, (uLong)iData.size()));
657 }
658
659 //设置压缩后大小与待处理大小
660 size_t szCompressedSize = 0;
661 size_t szRemainingSize = iData.size();
662 int iRet = Z_OK;
663 do
664 {
665 //计算剩余大小并在不足时扩容
666 size_t szOut = oData.size() - szCompressedSize;
667 if (szOut == 0)
668 {
669 oData.resize(oData.size() * 2);
670 szOut = oData.size() - szCompressedSize;
671 }
672
673 //获取新的地址
674 zs.next_out = (Bytef *)(&oData.data()[szCompressedSize]);
675
676 //如果输入被消耗完,重新赋值
677 if (zs.avail_in == 0)
678 {
679 constexpr uInt uIntMax = (uInt)-1;
680 zs.avail_in = szRemainingSize > (size_t)uIntMax ? uIntMax : (uInt)szRemainingSize;
681 szRemainingSize -= zs.avail_in;//缩小剩余待处理大小
682 }
683
684 //如果输出大小耗尽,重新赋值
685 if (zs.avail_out == 0)
686 {
687 constexpr uInt uIntMax = (uInt)-1;
688 zs.avail_out = szOut > (size_t)uIntMax ? uIntMax : (uInt)szOut;
689 //这里不对szOut处理,因为会在开头与结尾计算
690 }
691
692 //解压,如果剩余不为0则代表分块了,使用Z_NO_FLUSH,否则Z_FINISH
693 iRet = deflate(&zs, szRemainingSize != 0 ? Z_NO_FLUSH : Z_FINISH);
694
695 //计算本次压缩的大小
696 szCompressedSize += szOut - zs.avail_out;
697 } while (iRet == Z_OK || iRet == Z_BUF_ERROR);//只要没错误(缓冲区不够大除外)或者没到结尾就继续运行
698
699 //结束并设置大小
700 deflateEnd(&zs);
701 oData.resize(szCompressedSize);
702
703 //错误处理
704 if (iRet != Z_STREAM_END)
705 {
706 if (zs.msg != NULL)
707 {
708 throw std::runtime_error(std::string("Zlib compression failed with error message: ") + std::string(zs.msg));
709 }
710 else
711 {
712 throw std::runtime_error(std::string("Zlib compression failed with error code: ") + std::to_string(iRet));
713 }
714 }
715 }
716
728 template<typename I, typename O, typename ErrInfoFunc = NBT_Print>
729 requires (sizeof(typename I::value_type) == 1 && std::is_trivially_copyable_v<typename I::value_type> &&
730 sizeof(typename O::value_type) == 1 && std::is_trivially_copyable_v<typename O::value_type>)
731 static bool DecompressDataNoThrow(O &oData, const I &iData, ErrInfoFunc funcErrInfo = NBT_Print{ stderr }) noexcept
732 {
733 try
734 {
735 DecompressData(oData, iData);
736 return true;
737 }
738 catch (const std::bad_alloc &e)
739 {
740 funcErrInfo("std::bad_alloc:[{}]\n", e.what());
741 return false;
742 }
743 catch (const std::exception &e)
744 {
745 funcErrInfo("std::exception:[{}]\n", e.what());
746 return false;
747 }
748 catch (...)
749 {
750 funcErrInfo("Unknown Error\n");
751 return false;
752 }
753 }
754
767 template<typename I, typename O, typename ErrInfoFunc = NBT_Print>
768 requires (sizeof(typename I::value_type) == 1 && std::is_trivially_copyable_v<typename I::value_type> &&
769 sizeof(typename O::value_type) == 1 && std::is_trivially_copyable_v<typename O::value_type>)
770 static bool CompressDataNoThrow(O &oData, const I &iData, int iLevel = Z_DEFAULT_COMPRESSION, ErrInfoFunc funcErrInfo = NBT_Print{ stderr }) noexcept
771 {
772 try
773 {
774 CompressData(oData, iData, iLevel);
775 return true;
776 }
777 catch (const std::bad_alloc &e)
778 {
779 funcErrInfo("std::bad_alloc:[{}]\n", e.what());
780 return false;
781 }
782 catch (const std::exception &e)
783 {
784 funcErrInfo("std::exception:[{}]\n", e.what());
785 return false;
786 }
787 catch (...)
788 {
789 funcErrInfo("Unknown Error\n");
790 return false;
791 }
792 }
793
794#endif
795};
用于处理NBT信息打印的默认实现
DefaultInputStream(DefaultInputStream &&)=delete
禁止移动构造
DefaultInputStream(const T &&_tData, size_t szStartIdx=0)=delete
禁止使用临时对象构造
void UnGet() noexcept
回退一个字节的读取
定义 NBT_IO.hpp:110
size_t Index() const noexcept
获取当前读取位置(只读)
定义 NBT_IO.hpp:181
DefaultInputStream & operator=(const DefaultInputStream &)=delete
禁止拷贝赋值
~DefaultInputStream(void)=default
默认析构函数
typename T::value_type ValueType
容器值类型
定义 NBT_IO.hpp:54
const ValueType * CurData() const noexcept
获取当前读取位置的指针
定义 NBT_IO.hpp:121
size_t Size() const noexcept
获取流的总大小
定义 NBT_IO.hpp:153
T StreamType
容器类型
定义 NBT_IO.hpp:52
DefaultInputStream(const DefaultInputStream &)=delete
禁止拷贝构造
size_t SubIndex(size_t szSize) noexcept
向前撤销读取
定义 NBT_IO.hpp:139
const ValueType * BaseData() const noexcept
获取底层数据的起始指针
定义 NBT_IO.hpp:174
void Reset() noexcept
重置流读取位置到起始处
定义 NBT_IO.hpp:167
const ValueType & operator[](size_t szIndex) const noexcept
下标访问运算符
定义 NBT_IO.hpp:85
size_t & Index() noexcept
获取当前读取位置(可写)
定义 NBT_IO.hpp:189
DefaultInputStream & operator=(DefaultInputStream &&)=delete
禁止移动赋值
bool HasAvailData(size_t szSize) const noexcept
检查是否还有足够的数据可供读取
定义 NBT_IO.hpp:161
DefaultInputStream(const T &_tData, size_t szStartIdx=0)
构造函数
定义 NBT_IO.hpp:67
bool IsEnd() const noexcept
检查是否已到达流末尾
定义 NBT_IO.hpp:146
size_t AddIndex(size_t szSize) noexcept
向后推进读取
定义 NBT_IO.hpp:130
void GetRange(void *pDest, size_t szSize) noexcept
从流中读取一段数据
定义 NBT_IO.hpp:102
const ValueType & GetNext() noexcept
获取下一个字节并推进读取位置
定义 NBT_IO.hpp:93
DefaultOutputStream & operator=(DefaultOutputStream &&)=delete
禁止移动赋值
void PutRange(const ValueType *pData, size_t szSize)
向流中写入一段数据
定义 NBT_IO.hpp:262
void UnPut(void) noexcept
删除(撤销)最后一个写入的字节
定义 NBT_IO.hpp:282
typename T::value_type ValueType
容器值类型
定义 NBT_IO.hpp:212
const T & Data(void) const noexcept
获取底层数据的常量引用
定义 NBT_IO.hpp:302
T StreamType
容器类型
定义 NBT_IO.hpp:210
void PutOnce(V &&c)
向流中写入写入单个值
定义 NBT_IO.hpp:253
T & Data(void) noexcept
获取底层数据的非常量引用
定义 NBT_IO.hpp:309
DefaultOutputStream(DefaultOutputStream &&)=delete
禁止移动构造
size_t Size(void) const noexcept
获取当前字节流中已有的数据大小
定义 NBT_IO.hpp:289
DefaultOutputStream(T &_tData, size_t szStartIdx=0)
构造函数
定义 NBT_IO.hpp:222
DefaultOutputStream & operator=(const DefaultOutputStream &)=delete
禁止拷贝赋值
const ValueType & operator[](size_t szIndex) const noexcept
下标访问运算符
定义 NBT_IO.hpp:242
DefaultOutputStream(const DefaultOutputStream &)=delete
禁止拷贝构造
void AddReserve(size_t szAddSize)
预分配额外容量
定义 NBT_IO.hpp:276
~DefaultOutputStream(void)=default
默认析构函数
void Reset(void) noexcept
重置流,清空所有数据
定义 NBT_IO.hpp:295
static bool DecompressDataNoThrow(O &oData, const I &iData, ErrInfoFunc funcErrInfo=NBT_Print{ stderr }) noexcept
解压数据,但是不抛出异常,而是通过funcErrInfo打印异常信息并返回成功与否
定义 NBT_IO.hpp:731
static bool ReadFile(const std::filesystem::path &pathFileName, T &tData)
从指定文件名的文件中读取字节流数据到任意顺序容器中
定义 NBT_IO.hpp:357
static bool WriteFile(const std::filesystem::path &pathFileName, const T &tData)
从任意顺序容器写出字节流数据到指定文件名的文件中
定义 NBT_IO.hpp:326
static bool CompressDataNoThrow(O &oData, const I &iData, int iLevel=Z_DEFAULT_COMPRESSION, ErrInfoFunc funcErrInfo=NBT_Print{ stderr }) noexcept
压缩数据,但是不抛出异常,而是通过funcErrInfo打印异常信息并返回成功与否
定义 NBT_IO.hpp:770
static bool IsFileExist(const std::filesystem::path &pathFileName)
判断指定文件名的文件是否存在
定义 NBT_IO.hpp:400
static bool IsDataZipped(const T &tData)
判断一个顺序容器存储的字节流是否可能存在压缩
定义 NBT_IO.hpp:444
static void DecompressData(O &oData, const I &iData)
解压数据,自动判断Zlib或Gzip并解压,如果失败则抛出异常
定义 NBT_IO.hpp:467
static bool IsGzip(uint8_t u8DataFirst, uint8_t u8DataSecond)
通过字节流开始的两个字节判断是否可能是Gzip压缩
定义 NBT_IO.hpp:431
static bool IsZlib(uint8_t u8DataFirst, uint8_t u8DataSecond)
通过字节流开始的两个字节判断是否可能是Zlib压缩
定义 NBT_IO.hpp:415
static void CompressData(O &oData, const I &iData, int iLevel=Z_DEFAULT_COMPRESSION)
压缩数据,默认压缩为Gzip,也就是NBT格式的标准压缩类型,如果失败则抛出异常
定义 NBT_IO.hpp:586
一个用于打印信息到指定的C文件对象的工具类,作为库内大部分存在信息输出接口的默认实现。 实际可被使用此类为默认值参数的函数的调用方,以类似此类的仿函数参数重写的其它类型替换, 比如调用方实现了一个My_...
定义 NBT_Print.hpp:24
根据已安装的可选依赖提供定义