第2章この章では、前章でのトランザクション作成に続いて、Bitcoin クライアントがデータをシリアル化するプロセスについて説明します。 Bitcoin クライアントのすべてのシリアル化関数は、seriliaze.h に実装されています。その中でも、CDataStream クラスはデータシリアル化の中核構造です。 データストリームCDataStream には、シリアル化されたデータを格納するための文字クラス コンテナーがあります。コンテナ タイプとデータ処理用のストリーム インターフェイスを組み合わせます。この機能を実現するために、6 つのメンバー関数を使用します。 クラス CDataStream { 保護されています: typedef vector<char, secure_allocator<char> > vector_type; ベクトル型vch; 符号なし整数nReadPos; 短い状態; 短い exceptmask; 公共: int nType; int バージョン; //....... }
列挙型 { // 主なアクション SER_NETWORK = (1 << 0)、 SER_DISK = (1 << 1)、 SER_GETHASH = (1 << 2)、 // 修飾子 SER_SKIPSIG = (1 << 16)、 SER_BLOCKHEADERONLY = (1 << 17)、 };
CDataStream::read() および CDataStream::write()メンバー関数 CDataStream::read() および CDataStream::write() は、CDataStream オブジェクトのシリアル化/逆シリアル化を実行するために使用される低レベル関数です。 CDataStream& 読み取り(char* pch, int nSize) { // バッファの先頭から読み取る assert(nSize >= 0); 符号なし整数 nReadPosNext = nReadPos + nSize; (nReadPosNext >= vch.size()) の場合 { (nReadPosNext > vch.size()) の場合 { setstate(ios::failbit, "CDataStream::read() : データの終わり"); memset(pch, 0, nSize); nSize = vch.size() - nReadPos; } memcpy(pch, &vch[nReadPos], nSize); 読み取り位置 = 0; vch.clear(); (*これ) を返します。 } memcpy(pch, &vch[nReadPos], nSize); 読み取り位置 = 読み取り位置次へ; (*これ) を返します。 } CDataStream& write(const char* pch, int nSize) { // バッファの末尾に書き込む assert(nSize >= 0); vch.insert(vch.end(), pch, pch + nSize); (*これ) を返します。 } CDataStream::read() は、CDataStream から nSize 文字を char* pch が指すメモリ空間にコピーします。実装方法は次のとおりです。
この実装は、1) ストリームからデータの一部が読み取られた後は、再度読み取ることができないことを示しています。 2) nReadPos は最初の有効なデータの読み取り位置です。 CDataStream::write() は非常にシンプルです。 pch が指す nSize 文字を vch の末尾に追加します。 マクロ READDATA() および WRITEDATA()CDataStream::read() 関数と CDataStream::write() 関数は、プリミティブ型 (int、bool、unsigned long など) をシリアル化/逆シリアル化するために使用されます。これらのデータ型をシリアル化するには、これらの型のポインターを char* に変換します。これらの型のサイズがわかっているので、CDataStream から読み取ったり、文字バッファに書き込んだりすることができます。これらの関数を参照するために使用される 2 つのマクロがヘルパーとして定義されています。 #define WRITEDATA(s, obj) s.write((char*)&(obj), sizeof(obj)) #define READDATA(s, obj) s.read((char*)&(obj), sizeof(obj)) これらのマクロの使用方法の例を次に示します。次の関数は、unsigned long 型をシリアル化します。 テンプレート<typename Stream> inline void Serialize(Stream& s, unsigned long a, int, int=0) { WRITEDATA(s, a); } WRITEDATA(s, a) を独自の定義に置き換えます。拡張された関数は次のとおりです。 テンプレート<typename Stream> inline void Serialize(Stream& s, unsigned long a, int, int=0) { s.write((char*)&(a), sizeof(a)); } この関数は、unsigned long パラメータ a を受け取り、そのメモリ アドレスを取得し、ポインタを char* に変換して、関数 s.write() を呼び出します。 CDataStream の演算子 << と >>CDataStream は、シリアル化と逆シリアル化のために演算子 << と >> をオーバーロードします。 テンプレート<typename T> CDataStream& 演算子<<(const T& obj) { // このストリームにシリアル化します ::Serialize(*this, obj, nType, nVersion); (*これ) を返します。 } テンプレート<typename T> CDataStream& 演算子>>(T& オブジェクト) { // このストリームからアンシリアル化します ::Unserialize(*this, obj, nType, nVersion); (*これ) を返します。 } ヘッダー ファイル serialize.h には、14 個のプリミティブ型 (char、short、int、long、long long の符号付きおよび符号なしバージョン、および char、float、double、bool) に対するこれら 2 つのグローバル関数の 14 個のオーバーロードと、6 個の複合型 (string、vector、pair、map、set、CScript) に対する 6 個のオーバーロードが含まれています。したがって、これらの型の場合、次のコードを使用するだけでデータをシリアル化/逆シリアル化できます。 CDataStream ss(SER_GETHASH); ss<<obj1<<obj2; //シリアル化 ss>>obj3>>obj4; //デシリアライズ 2 番目の引数 obj に一致する実装タイプがない場合は、次の汎用 T グローバル関数が呼び出されます。 テンプレート<typename Stream, typename T> インライン void Serialize(Stream& os, const T& a, long nType, int nVersion=VERSION) { a.Serialize(os, (int)nType, nVersion); } このジェネリック バージョンでは、シグネチャ T::Serialize(Stream, int, int) を持つメンバー関数を実装するために、型 T を使用する必要があります。これは a.Serialize() を介して呼び出されます。 型をシリアル化する方法前回の紹介では、ジェネリック型 T はシリアル化のために次の 3 つのメンバー関数を実装する必要があります。 符号なし int GetSerializeSize(int nType=0, int nVersion=VERSION) const; void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const; void アンシリアル化(Stream& s, int nType=0, int nVersion=VERSION); これら 3 つの関数は、ジェネリック型 T を持つ対応するグローバル関数によって呼び出されます。これらのグローバル関数は、CDataStream のオーバーロードされた演算子 << および >> によって呼び出されます。 マクロ IMPLEMENT_SERIALIZE(statements) は、任意の型に対してこれら 3 つの関数の実装を定義するために使用されます。 #define IMPLEMENT_SERIALIZE(ステートメント) \ 符号なし int GetSerializeSize(int nType=0, int nVersion=VERSION) const \ {\ CSerActionGetSerializeSize ser_action; \ 定数ブール fGetSize = true; \ 定数ブールfWrite = false; \ 定数ブール fRead = false; \ 符号なし整数nSerSize = 0; \ ser_streamプレースホルダーs; \ s.nType = nType; \ s.nVersion = nVersion; \ {ステートメント} \ nSerSize を返します。 \ } \ テンプレート<typename Stream> \ void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const \ {\ CSerActionser_action をシリアル化します。 \ 定数ブール fGetSize = false; \ 定数ブールfWrite = true; \ 定数ブール fRead = false; \ 符号なし整数nSerSize = 0; \ {ステートメント} \ } \ テンプレート<typename Stream> \ void アンシリアル化(Stream& s, int nType=0, int nVersion=VERSION) \ {\ CSerActionser_action をアンシリアル化します。 \ 定数ブール fGetSize = false; \ 定数ブールfWrite = false; \ 定数ブール fRead = true; \ 符号なし整数nSerSize = 0; \ {ステートメント} \ } 次の例は、このマクロの使用方法を示しています。 #include <iostream> #include "serialize.h" 名前空間 std を使用します。 クラスAClass{ 公共: AClass(int xin) : x(xin){}; 整数x; IMPLEMENT_SERIALIZE(READWRITE(this->x);) } int main() { CDataStream astream2; クラスaObj(200); // x が 200 の AClass 型オブジェクト cout<<"aObj="<<aObj.x>>endl; asream2<<aObj; Aクラスa2(1) // x が 1 である別のオブジェクト astream2>>a2 cout<<"a2="<<a2.x<<endl; 0を返します。 } このプログラムは、AClass オブジェクトをシリアル化/デシリアル化します。次の結果が画面に出力されます。 200 のオブジェクト a2=200 AClass の次の 3 つのシリアル化/逆シリアル化メンバー関数は、1 行のコードで実装できます。 IMPLEMENT_SERIALIZE(READWRITE(this->x);) マクロREADWRITE()の定義は次のとおりです。 #define READWRITE(obj) (nSerSize += ::SerReadWrite(s, (obj), nType, nVersion, ser_action)) このマクロの展開は、マクロ IMPLEMENT_SERIALIZE(ステートメント) の 3 つの関数すべてに配置されます。したがって、一度に次の 3 つのことを行う必要があります。1) シリアル化されたデータのサイズを返す、2) データをストリームにシリアル化 (書き込む) する。 3) ストリームからデータを逆シリアル化 (読み取り) します。マクロ IMPLEMENT_SERIALIZE(ステートメント) のこれらの 3 つの関数の定義を参照してください。 マクロ READWRITE(obj) がどのように機能するかを理解するには、まず、nSerSize、s、nType、nVersion、および ser_action が完全な形式でどこから来るのかを理解する必要があります。これらはすべて、マクロ IMPLEMENT_SERIALIZE(ステートメント) の 3 つの関数本体から取得されます。
したがって、マクロ READWRITE() がマクロ IMPLEMENT_SERIALIZE() に展開されると、そのシンボルはすべてマクロ IMPLEMENT_SERIALIZE() の本体にすでに存在しているため、評価されます。 READWRITE(obj) の拡張は、グローバル関数::SerReadWrite(s, (obj), nType, nVersion, ser_action) を呼び出します。この関数の 3 つのバージョンをすべて次に示します。 テンプレート<typename Stream, typename T> インライン unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionGetSerializeSize ser_action) { 戻り値::GetSerializeSize(obj, nType, nVersion); } テンプレート<typename Stream, typename T> インライン unsigned int SerReadWrite(Stream& s, const T& obj, int nType, int nVersion, CSerActionSerialize ser_action) { ::Serialize(s, obj, nType, nVersion); 0を返します。 } テンプレート<typename Stream, typename T> インライン unsigned int SerReadWrite(Stream& s, T& obj, int nType, int nVersion, CSerActionUnserialize ser_action) { ::Unserialize(s, obj, nType, nVersion); 0を返します。 } ご覧のとおり、関数 ::SerReadWrite() は 3 つのバージョンにオーバーロードされています。最後のパラメータに応じて、それぞれグローバル関数 ::GetSerialize()、::Serialize()、::Unserialize() を呼び出します。これら 3 つの機能は前の章で紹介しました。 ::SerReadWrite() の 3 つの異なるバージョンの最後のパラメータを調べると、それらはすべて void 型であることがわかります。これら 3 つのタイプの唯一の目的は、マクロ IMPLEMENT_SERIALIZE() によって定義されるすべての関数によって使用される ::SerReadWrite() の 3 つのバージョンを区別することです。 |
<<: 日本がビットコイン価格を新たな高値に導き、史上最高値の1,277ドルに迫る
>>: テンセントはブロックチェーンのホワイトペーパーを発表し、エンタープライズレベルのブロックチェーンインフラプラットフォームの構築を目指す
インターネットの発展の歴史を振り返ると、Web1段階で伝統的な広告産業のデジタル化が完了し、Web2...
Googleは、更新された金融商品およびサービスに関するポリシーが8月3日に全面的に発効した後、再び...
Baozou Timesのコメント:新興のブロックチェーン分野では概念実証は非常に一般的です。新しい...
Bitmain初の7nmチップを搭載したAntminer T15は2018年11月に発売され、その...
ベルギーの税務当局は暗号通貨への課税を真剣に受け止め始めている。ブリュッセル・タイムズによると、ベル...
クレイジーな解説: 今日、ブロックチェーン技術を基盤とする医療企業がゲームのルールを再定義しています...
ブロックチェーンの開始 – 2017 年半ば (2017 年 5 月 8 日) X12 は安全で、プ...
ウー・サイード著者 |シュルデンこの号の編集者 |コリン・ウー少し前に、Chia Network (...
金融はブロックチェーンが最も簡単に実装できるシナリオです。過去 1 年間、イーサリアム エコシステム...
クレイジーな解説: ビットコインのフォーク傾向が強まる中、両陣営は、どちらかが極端な行動をとった場合...
エルサルバドル大統領は金を稼いだ2024年2月29日、エルサルバドルのナジブ・ブケレ大統領は、ビット...
暗号資産市場は不安定になりつつあり、マクロ要因が再び市場の流れを支配しています。ビットコインは底を打...
360 Network Security Research Institute は、世界初の And...
本日をもって、シンガポールのWeb3ワークの旅も終了しました。実際、実践者としては昨日終わりました。...
1 DEFIは下落を止めたが、買えるか? DeFiは最近急激に下落しており、多くは最高値から90%...