Open-vcdiffストリーム符号化プロセス分析(三)
9785 ワード
基本原理を理解して、コードを見ると楽になり、EncodeInternalの完全なコードを貼り付けます.
冒頭の数行は、前文で分析しましたが、33行からブロックマッチングの論理です.まずいくつかの変数を初期化します:next_Encodeは符号化されたデータの起点である.candidate_posは現在のウィンドウの起点であり、前に紹介したように、最初の16 byteがblockに一致しなければ、ウィンドウが移動します.target_endは今回の関数呼び出しで伝達されたデータのポイントであり,ウィンドウの右端が終点に着いたら,今回の符号化は終了する.
次に、EncodeCopyForBestMatch関数、すなわち、前に説明したように、現在のウィンドウの16 byteに基づいて、一致するblockを見つけ、その後、最も長い一致するblockが見つかるまで、そのコンテキストをできるだけ一致させ、その後、COPY符号化を行い、COPYの前にADDを持つことができる.EncodeCopyForBestMatchの戻り値は、見つかった最適なmatchデータの長さです.
if(bytes_encoded>0)、このブランチは、見つかったmatchデータを表し、符号化に成功した.ここの論理はnext_ですencode,candidate_posはそれぞれ後方に移動し,後方に移動した値はmathデータの長さであり,処理が完了しているかどうかを確認し,終了していない場合は新しいウィンドウのハッシュを計算する.次のif(look_for_target_matchs)は、targetデータ自体にmatchデータを探す必要がある場合、符号化されたデータも、target_と呼ばれるセグメントハッシュを確立すべきであることを示す.hash_.
Elseブランチは、matchが見つからないことを示します(または、見つかったmatchが短すぎて、一度のCOPY符号化に値しません).ここの論理、candidate_pos_1を後ろに移動し、hashを再計算します.次に、自己マッチングが必要な場合はtarget_を追加します.hash、ここでは少し迷いがありますが、byteを移動するたびにblockを追加する必要はありません.AddOneIndexHashのコードを見るとわかります.以下の注釈部分とif判断に注意してください.
ループが終了すると、最後に残ったblock未満のデータを関数AddUnmatchedRemainderによりVCDiffのADDに直接符号化し、FinishEncodingによりバッファを書き込みます.
次に、ループの先頭にあるEncodeCopyForBestMatch関数を見てみましょう.コード:
この関数自体はあまり情報量がなく,まずFindBestMatchを呼び出し,最適なmatchデータを見つけ,次いでADD,COPYである.メインイベントはFindBestMatchに任せました.この関数のコードを見てみましょう.
未完,下編を参照
template<bool look_for_target_matches>
void VCDiffEngine::EncodeInternal(const char* target_data,
size_t target_size,
OutputStringInterface* diff,
CodeTableWriterInterface* coder) const {
if (!hashed_dictionary_) {
VCD_DFATAL << "Internal error: VCDiffEngine::Encode() "
"called before VCDiffEngine::Init()" << VCD_ENDL;
return;
}
if (target_size == 0) {
return; // Do nothing for empty target
}
// Special case for really small input
if (target_size < static_cast<size_t>(BlockHash::kBlockSize)) {
AddUnmatchedRemainder(target_data, target_size, coder);
FinishEncoding(target_size, diff, coder);
return;
}
RollingHash<BlockHash::kBlockSize> hasher;
BlockHash* target_hash = NULL;
if (look_for_target_matches) {
// Check matches against previously encoded target data
// in this same target window, as well as against the dictionary
target_hash = BlockHash::CreateTargetHash(target_data,
target_size,
dictionary_size());
if (!target_hash) {
VCD_DFATAL << "Instantiation of target hash failed" << VCD_ENDL;
return;
}
}
const char* const target_end = target_data + target_size;
const char* const start_of_last_block = target_end - BlockHash::kBlockSize;
// Offset of next bytes in string to ADD if NOT copied (i.e., not found in
// dictionary)
const char* next_encode = target_data;
// candidate_pos points to the start of the kBlockSize-byte block that may
// begin a match with the dictionary or previously encoded target data.
const char* candidate_pos = target_data;
uint32_t hash_value = hasher.Hash(candidate_pos);
while (1) {
const size_t bytes_encoded =
EncodeCopyForBestMatch<look_for_target_matches>(
hash_value,
candidate_pos,
next_encode,
(target_end - next_encode),
target_hash,
coder);
if (bytes_encoded > 0) {
next_encode += bytes_encoded; // Advance past COPYed data
candidate_pos = next_encode;
if (candidate_pos > start_of_last_block) {
break; // Reached end of target data
}
// candidate_pos has jumped ahead by bytes_encoded bytes, so UpdateHash
// can't be used to calculate the hash value at its new position.
hash_value = hasher.Hash(candidate_pos);
if (look_for_target_matches) {
// Update the target hash for the ADDed and COPYed data
target_hash->AddAllBlocksThroughIndex(
static_cast<int>(next_encode - target_data));
}
} else {
// No match, or match is too small to be worth a COPY instruction.
// Move to the next position in the target data.
if ((candidate_pos + 1) > start_of_last_block) {
break; // Reached end of target data
}
if (look_for_target_matches) {
target_hash->AddOneIndexHash(
static_cast<int>(candidate_pos - target_data),
hash_value);
}
hash_value = hasher.UpdateHash(hash_value,
candidate_pos[0],
candidate_pos[BlockHash::kBlockSize]);
++candidate_pos;
}
}
AddUnmatchedRemainder(next_encode, target_end - next_encode, coder);
FinishEncoding(target_size, diff, coder);
delete target_hash;
}
冒頭の数行は、前文で分析しましたが、33行からブロックマッチングの論理です.まずいくつかの変数を初期化します:next_Encodeは符号化されたデータの起点である.candidate_posは現在のウィンドウの起点であり、前に紹介したように、最初の16 byteがblockに一致しなければ、ウィンドウが移動します.target_endは今回の関数呼び出しで伝達されたデータのポイントであり,ウィンドウの右端が終点に着いたら,今回の符号化は終了する.
次に、EncodeCopyForBestMatch関数、すなわち、前に説明したように、現在のウィンドウの16 byteに基づいて、一致するblockを見つけ、その後、最も長い一致するblockが見つかるまで、そのコンテキストをできるだけ一致させ、その後、COPY符号化を行い、COPYの前にADDを持つことができる.EncodeCopyForBestMatchの戻り値は、見つかった最適なmatchデータの長さです.
if(bytes_encoded>0)、このブランチは、見つかったmatchデータを表し、符号化に成功した.ここの論理はnext_ですencode,candidate_posはそれぞれ後方に移動し,後方に移動した値はmathデータの長さであり,処理が完了しているかどうかを確認し,終了していない場合は新しいウィンドウのハッシュを計算する.次のif(look_for_target_matchs)は、targetデータ自体にmatchデータを探す必要がある場合、符号化されたデータも、target_と呼ばれるセグメントハッシュを確立すべきであることを示す.hash_.
Elseブランチは、matchが見つからないことを示します(または、見つかったmatchが短すぎて、一度のCOPY符号化に値しません).ここの論理、candidate_pos_1を後ろに移動し、hashを再計算します.次に、自己マッチングが必要な場合はtarget_を追加します.hash、ここでは少し迷いがありますが、byteを移動するたびにblockを追加する必要はありません.AddOneIndexHashのコードを見るとわかります.以下の注釈部分とif判断に注意してください.
// This function will be called to add blocks incrementally to the target hash
// as the encoding position advances through the target data. It will be
// called for every kBlockSize-byte block in the target data, regardless
// of whether the block is aligned evenly on a block boundary. The
// BlockHash will only store hash entries for the evenly-aligned blocks.
//
void AddOneIndexHash(int index, uint32_t hash_value) {
if (index == NextIndexToAdd()) {
AddBlock(hash_value);
}
}
ループが終了すると、最後に残ったblock未満のデータを関数AddUnmatchedRemainderによりVCDiffのADDに直接符号化し、FinishEncodingによりバッファを書き込みます.
次に、ループの先頭にあるEncodeCopyForBestMatch関数を見てみましょう.コード:
template<bool look_for_target_matches>
inline size_t VCDiffEngine::EncodeCopyForBestMatch(
uint32_t hash_value,
const char* target_candidate_start,
const char* unencoded_target_start,
size_t unencoded_target_size,
const BlockHash* target_hash,
CodeTableWriterInterface* coder) const {
// When FindBestMatch() comes up with a match for a candidate block,
// it will populate best_match with the size, source offset,
// and target offset of the match.
BlockHash::Match best_match;
// First look for a match in the dictionary.
hashed_dictionary_->FindBestMatch(hash_value,
target_candidate_start,
unencoded_target_start,
unencoded_target_size,
&best_match);
// If target matching is enabled, then see if there is a better match
// within the target data that has been encoded so far.
if (look_for_target_matches) {
target_hash->FindBestMatch(hash_value,
target_candidate_start,
unencoded_target_start,
unencoded_target_size,
&best_match);
}
if (!ShouldGenerateCopyInstructionForMatchOfSize(best_match.size())) {
return 0;
}
if (best_match.target_offset() > 0) {
// Create an ADD instruction to encode all target bytes
// from the end of the last COPY match, if any, up to
// the beginning of this COPY match.
coder->Add(unencoded_target_start, best_match.target_offset());
}
coder->Copy(best_match.source_offset(), best_match.size());
return best_match.target_offset() // ADD size
+ best_match.size(); // + COPY size
}
この関数自体はあまり情報量がなく,まずFindBestMatchを呼び出し,最適なmatchデータを見つけ,次いでADD,COPYである.メインイベントはFindBestMatchに任せました.この関数のコードを見てみましょう.
void BlockHash::FindBestMatch(uint32_t hash_value,
const char* target_candidate_start,
const char* target_start,
size_t target_size,
Match* best_match) const {
int match_counter = 0;
for (int block_number = FirstMatchingBlockInline(hash_value,
target_candidate_start);
(block_number >= 0) && !TooManyMatches(&match_counter);
block_number = NextMatchingBlock(block_number, target_candidate_start)) {
int source_match_offset = block_number * kBlockSize;
const int source_match_end = source_match_offset + kBlockSize;
int target_match_offset =
static_cast<int>(target_candidate_start - target_start);
const int target_match_end = target_match_offset + kBlockSize;
size_t match_size = kBlockSize;
{
// Extend match start towards beginning of unencoded data
const int limit_bytes_to_left = std::min(source_match_offset,
target_match_offset);
const int matching_bytes_to_left =
MatchingBytesToLeft(source_data_ + source_match_offset,
target_start + target_match_offset,
limit_bytes_to_left);
source_match_offset -= matching_bytes_to_left;
target_match_offset -= matching_bytes_to_left;
match_size += matching_bytes_to_left;
}
{
// Extend match end towards end of unencoded data
const size_t source_bytes_to_right = source_size_ - source_match_end;
const size_t target_bytes_to_right = target_size - target_match_end;
const size_t limit_bytes_to_right = std::min(source_bytes_to_right,
target_bytes_to_right);
match_size +=
MatchingBytesToRight(source_data_ + source_match_end,
target_start + target_match_end,
static_cast<int>(limit_bytes_to_right));
}
// Update in/out parameter if the best match found was better
// than any match already stored in *best_match.
best_match->ReplaceIfBetterMatch(match_size,
source_match_offset + starting_offset_,
target_match_offset);
}
}
未完,下編を参照