第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ドルに迫る
>>: テンセントはブロックチェーンのホワイトペーパーを発表し、エンタープライズレベルのブロックチェーンインフラプラットフォームの構築を目指す
本日、グレイスケールはビットコインの史上最大の1日あたりのロック解除を実現し、約16,240 BTC...
【活動テーマ】涼しい貸出の秋が到来し、BTCC貸出ユーザーは独占的に「快適な雲南」旅行を楽しんでい...
ブロックチェーン・マネー・カンファレンスが最近ロンドンで開催され、ビットコインの創始者ロジャー・バー...
従来の通貨に対する野心的な挑戦者であるビットコインは、3月に4,000ドルを下回ったが、現在は19,...
2014年と2015年に、アジア、米国、欧州の金融規制当局は、為替市場の価格操作に協力したとして、...
第2四半期のウォール街での取引初日、米国の新たな経済データを受けて投資家が今年の金利引き下げへの期待...
ブラッド・ベーレンズDeFiとは何か分散型金融 (DeFi) は、主に Ethereum 上で展開さ...
PANewsは9月3日、OKLink Chain Masterのデータによると、本日14時20分に...
ビットコインは危機に瀕している。危機は碑文から始まった。鉱山労働者、開発業者、意見の異なる地域住民、...
北京時間12月2日早朝、米国証券取引委員会(SEC)は、仮想通貨を餌として使い、一攫千金を狙って投資...
億万長者のサム・バンクマン・フリード氏が仮想通貨デリバティブの批判に反論出典: フォーブススティーブ...
ビットコインの価格は2月22日以来初めて1コインあたり5万8000ドルを超え、先月記録した最高値に近...
ビットコインに対抗するために、ますます多くの中央銀行が独自のデジタル通貨の創設を議論し始めており、...
ビットコインの価値は頻繁に上下しているにもかかわらず、ウォール街やシリコンバレーの多くの買い手が、米...
グラフィック カード マイナーを使用して Ethereum をマイニングする人の多くは、DAG ファ...