
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// 1ブロックずつ計算するバージョン

static DWORD WINAPI thread_decode2_single(LPVOID lpParameter)
{
	unsigned char *buf, *p_buf, *work_buf;
	unsigned short *factor;
	int i, j, th_id, part_num, chunk_num, max_num, left_num, left_max;
	unsigned int unit_size, len, off, chunk_size;
	HANDLE hRun, hEnd;
	RS_TH *th;
#ifdef TIMER
unsigned int loop_count2a = 0, loop_count2b = 0;
unsigned int time_start2, time_encode2a = 0, time_encode2b = 0;
#endif

	th = (RS_TH *)lpParameter;
	buf = th->buf;
	unit_size = th->size;
	part_num = th->count;
	chunk_size = th->off;
	th_id = th->now;	// スレッド番号
	hRun = th->run;
	hEnd = th->end;
	//_mm_sfence();
	SetEvent(hEnd);	// 設定完了を通知する

	p_buf = buf + (size_t)unit_size * (source_num + cpu_num - 1);
	chunk_num = (unit_size + chunk_size - 1) / chunk_size;
	max_num = chunk_num * part_num;
	if (th_id > 0)
		work_buf = buf + (size_t)unit_size * (source_num + th_id - 1);

	WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	while (th->now < INT_MAX / 2){
#ifdef TIMER
time_start2 = GetTickCount();
#endif
		i = th->off;	// ソース・ブロック番号
		factor = th->mat;
		len = chunk_size;
		if (th->buf == NULL){	// ソース・ブロック読み込み中
			// 復元するブロックごとに掛け算して追加していく
			while ((j = InterlockedIncrement(&(th->now))) < max_num){	// j = ++th_now
				off = j / part_num;	// chunk の番号
				j = j % part_num;	// lost block の番号
				off *= chunk_size;
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				if (i == 0)	// 最初のブロックを計算する際に
					memset(p_buf + ((size_t)unit_size * j + off), 0, len);	// ブロックを 0で埋める
				galois_align_multiply(buf + ((size_t)unit_size * i + off), p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]);
#ifdef TIMER
loop_count2a++;
#endif
			}
#ifdef TIMER
time_encode2a += GetTickCount() - time_start2;
#endif
		} else {	// 復元したブロック書き込み中
			if (th_id == 0){
				work_buf = th->buf;
				if (i == 0)	// 最初から計算する場合はブロックを 0で埋める
					memset(work_buf, 0, unit_size);
			} else {
				memset(work_buf, 0, unit_size);	// ブロックを 0で埋める
			}
			left_num = source_num - i;	// 残りブロック数
			left_max = chunk_num * left_num;
			// ソース・ブロックごとに掛け算して追加していく
			while ((j = InterlockedIncrement(&(th->now))) < left_max){	// j = ++th_now
				off = j / left_num;	// chunk の番号
				j = j % left_num;	// source の番号
				off *= chunk_size;
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				galois_align_multiply(buf + ((size_t)unit_size * (i + j) + off), work_buf + off, len, factor[i + j]);
#ifdef TIMER
loop_count2b++;
#endif
			}
#ifdef TIMER
time_encode2b += GetTickCount() - time_start2;
#endif
		}
		//_mm_sfence();	// メモリーへの書き込みを完了する
		SetEvent(hEnd);	// 計算終了を通知する
		WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	}
#ifdef TIMER
printf("sub-thread[%d] : total loop = %d\n", th_id, loop_count2a + loop_count2b);
if (time_encode2a > 0){
	i = (int)((__int64)loop_count2a * chunk_size * 125 / ((__int64)time_encode2a * 131072));
} else {
	i = 0;
}
if (loop_count2a > 0)
	printf(" 1st decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i);
if (time_encode2b > 0){
	i = (int)((__int64)loop_count2b * chunk_size * 125 / ((__int64)time_encode2b * 131072));
} else {
	i = 0;
}
printf(" 2nd decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i);
#endif

	// 終了処理
	CloseHandle(hRun);
	CloseHandle(hEnd);
	return 0;
}

static DWORD WINAPI thread_decode3_single(LPVOID lpParameter)
{
	unsigned char *buf, *p_buf, *work_buf;
	unsigned short *factor;
	int i, j, th_id, block_lost, chunk_num, max_num, left_num, left_max;
	unsigned int unit_size, len, off, chunk_size;
	HANDLE hRun, hEnd;
	RS_TH *th;
#ifdef TIMER
unsigned int loop_count2a = 0, loop_count2b = 0;
unsigned int time_start2, time_encode2a = 0, time_encode2b = 0;
#endif

	th = (RS_TH *)lpParameter;
	buf = th->buf;
	block_lost = th->size;
	left_num = th->count;
	chunk_size = th->off;
	th_id = th->now;	// スレッド番号
	hRun = th->run;
	hEnd = th->end;
	//_mm_sfence();
	SetEvent(hEnd);	// 設定完了を通知する

	unit_size = (block_size + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1);
	p_buf = buf + (size_t)unit_size * (left_num + cpu_num - 1);
	chunk_num = (unit_size + chunk_size - 1) / chunk_size;
	max_num = chunk_num * block_lost;
	if (th_id > 0)
		work_buf = buf + (size_t)unit_size * (left_num + th_id - 1);

	WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	while (th->now < INT_MAX / 2){
#ifdef TIMER
time_start2 = GetTickCount();
#endif
		i = th->off;	// ソース・ブロック番号
		factor = th->mat;
		len = chunk_size;
		if (th->buf == NULL){	// ソース・ブロック読み込み中
			left_num = th->count + i;
			// 復元するブロックごとに掛け算して追加していく
			while ((j = InterlockedIncrement(&(th->now))) < max_num){	// j = ++th_now
				off = j / block_lost;	// chunk の番号
				j = j % block_lost;		// lost block の番号
				off *= chunk_size;
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				if (left_num == 0)
					memset(p_buf + ((size_t)unit_size * j + off), 0, len);	// ブロックを 0で埋める
				galois_align_multiply(buf + ((size_t)unit_size * i + off), p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]);
#ifdef TIMER
loop_count2a++;
#endif
			}
#ifdef TIMER
time_encode2a += GetTickCount() - time_start2;
#endif
		} else {	// 復元したブロック書き込み中
			if (th_id == 0){
				work_buf = th->buf;
			} else {
				memset(work_buf, 0, unit_size);	// ブロックを 0で埋める
			}
			left_num = th->count - i;	// 残りブロック数
			left_max = chunk_num * left_num;
			// ソース・ブロックごとに掛け算して追加していく
			while ((j = InterlockedIncrement(&(th->now))) < left_max){	// j = ++th_now
				off = j / left_num;	// chunk の番号
				j = j % left_num;	// source の番号
				off *= chunk_size;
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				galois_align_multiply(buf + ((size_t)unit_size * (i + j) + off), work_buf + off, len, factor[i + j]);
#ifdef TIMER
loop_count2b++;
#endif
			}
#ifdef TIMER
time_encode2b += GetTickCount() - time_start2;
#endif
		}
		//_mm_sfence();	// メモリーへの書き込みを完了する
		SetEvent(hEnd);	// 計算終了を通知する
		WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	}
#ifdef TIMER
printf("sub-thread[%d] : total loop = %d\n", th_id, loop_count2a + loop_count2b);
if (time_encode2a > 0){
	i = (int)((__int64)loop_count2a * chunk_size * 125 / ((__int64)time_encode2a * 131072));
} else {
	i = 0;
}
if (loop_count2a > 0)
	printf(" 1st encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i);
if (time_encode2b > 0){
	i = (int)((__int64)loop_count2b * chunk_size * 125 / ((__int64)time_encode2b * 131072));
} else {
	i = 0;
}
printf(" 2nd encode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i);
#endif

	// 終了処理
	CloseHandle(hRun);
	CloseHandle(hEnd);
	return 0;
}


int decode_method2_single(	// ソース・データを全て読み込む場合
	wchar_t *file_path,
	int block_lost,			// 失われたソース・ブロックの数
	HANDLE *rcv_hFile,		// リカバリ・ファイルのハンドル
	file_ctx_r *files,		// ソース・ファイルの情報
	source_ctx_r *s_blk,	// ソース・ブロックの情報
	parity_ctx_r *p_blk,		// パリティ・ブロックの情報
	unsigned short *mat)
{
	unsigned char *buf = NULL, *p_buf, *work_buf, *sub_buf, *hash;
	unsigned short *id;
	int err = 0, i, j, k, last_file, part_num, recv_now;
	unsigned int io_size, unit_size, len, block_off;
	unsigned int time_last;
	__int64 file_off, prog_num = 0, prog_base;
	HANDLE hFile = NULL;
	HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU];
	RS_TH th[1];

	memset(hSub, 0, sizeof(HANDLE) * (MAX_CPU));
	id = mat + (block_lost * source_num);	// 何番目の消失ソース・ブロックがどのパリティで代替されるか

	// 作業バッファーを確保する
	part_num = source_num >> PART_MAX_RATE;	// ソース・ブロック数に対する割合で最大量を決める
	if (part_num > block_lost)
		part_num = block_lost;
	i = source_num + (cpu_num - 1);	// ソース・ブロックとサブ・スレッドの作業領域が必要
	io_size = get_io_size(i, &part_num, 1, sse_unit);
	//io_size = (((io_size + 2) / 3 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE;	// 実験用
	unit_size = io_size + HASH_SIZE;	// チェックサムの分だけ増やす
	file_off = (i + part_num) * (size_t)unit_size + HASH_SIZE;
	buf = _aligned_malloc((size_t)file_off, sse_unit);
	if (buf == NULL){
		printf("malloc, %I64d\n", file_off);
		err = 1;
		goto error_end;
	}
	sub_buf = buf + (size_t)unit_size * source_num;	// サブ・スレッドの作業領域
	p_buf = sub_buf + (size_t)unit_size * (cpu_num - 1);	// 復元したブロックを部分的に記録する領域
	hash = p_buf + (size_t)unit_size * part_num;
	prog_base = (block_size + io_size - 1) / io_size;
	prog_base *= source_num * block_lost;	// 全体の断片の個数
#ifdef TIMER
	printf("\n read all blocks, and keep some recovering blocks (single)\n");
	printf("buffer size = %I64d MB, io_size = %d, split = %d\n", file_off >> 20, io_size, (block_size + io_size - 1) / io_size);
	len = try_cache_blocking(unit_size);
	printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, (unit_size + len - 1) / len);
	printf("prog_base = %I64d, part_num = %d\n", prog_base, part_num);
#endif

	// マルチ・スレッドの準備をする
	th->buf = buf;
	th->size = unit_size;
	th->count = part_num;
	th->off = try_cache_blocking(unit_size);	// キャッシュの最適化を試みる
	for (j = 0; j < cpu_num; j++){	// サブ・スレッドごとに
		hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL);	// Auto Reset にする
		if (hRun[j] == NULL){
			print_win32_err();
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (hEnd[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		// サブ・スレッドを起動する
		th->run = hRun[j];
		th->end = hEnd[j];
		th->now = j;	// スレッド番号
		//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを起動する
		hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode2_single, (LPVOID)th, 0, NULL);
		if (hSub[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			CloseHandle(hEnd[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		WaitForSingleObject(hEnd[j], INFINITE);	// 設定終了の合図を待つ (リセットしない)
	}
	// IO が延滞しないように、サブ・スレッド一つの優先度を下げる
	SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL);

	// バッファー・サイズごとにソース・ブロックを復元する
	print_progress_text(0, "Recovering slice");
	time_last = GetTickCount();
	wcscpy(file_path, base_dir);
	block_off = 0;
	while (block_off < block_size){
		th->buf = NULL;
		th->off = -1;	// まだ計算して無い印

#ifdef TIMER
read_count = 0;
skip_count = 0;
time_start = GetTickCount();
#endif
		last_file = -1;
		recv_now = 0;	// 何番目の代替ブロックか
		for (i = 0; i < source_num; i++){
			switch(s_blk[i].exist){
			case 0:		// バッファーにパリティ・ブロックの内容を読み込む
				len = block_size - block_off;
				if (len > io_size)
					len = io_size;
				file_off = p_blk[id[recv_now]].off + (__int64)block_off;
				if (file_read_data(rcv_hFile[p_blk[id[recv_now]].file], file_off, buf + (size_t)unit_size * i, len)){
					printf("file_read_data, recovery slice %d\n", id[recv_now]);
					err = 1;
					goto error_end;
				}
				if (len < io_size)
					memset(buf + ((size_t)unit_size * i + len), 0, io_size - len);
				recv_now++;
				// パリティ・ブロックのチェックサムを計算する
				checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size);
#ifdef TIMER
read_count++;
#endif
				break;
			case 3:		// ソース・ブロックの内容は全て 0
				len = 0;
				memset(buf + (size_t)unit_size * i, 0, unit_size);
				break;
			default:	// バッファーにソース・ブロックの内容を読み込む
				if (s_blk[i].file != last_file){	// 別のファイルなら開く
					last_file = s_blk[i].file;
					if (hFile){
						CloseHandle(hFile);	// 前のファイルを閉じる
						hFile = NULL;
					}
					if ((files[last_file].state & 7) != 0){	// 作り直した作業ファイルから読み込む
						get_temp_name(list_buf + files[last_file].name, file_path + base_len);
					} else if ((files[last_file].state & 32) != 0){	// 名前訂正失敗時には別名ファイルから読み込む
						wcscpy(file_path + base_len, list_buf + files[last_file].name2);
					} else {	// 完全なソース・ファイルから読み込む
						wcscpy(file_path + base_len, list_buf + files[last_file].name);
					}
					hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
					if (hFile == INVALID_HANDLE_VALUE){
						print_win32_err();
						hFile = NULL;
						printf_cp("cannot open file, %s\n", file_path);
						err = 1;
						goto error_end;
					}
				}
				if (s_blk[i].size > block_off){
					len = s_blk[i].size - block_off;
					if (len > io_size)
						len = io_size;
					file_off = (i - files[last_file].b_off) * (__int64)block_size + (__int64)block_off;
					if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){
						printf("file_read_data, input slice %d\n", i);
						err = 1;
						goto error_end;
					}
					if (len < io_size)
						memset(buf + ((size_t)unit_size * i + len), 0, io_size - len);
					// ソース・ブロックのチェックサムを計算する
					checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + io_size), io_size);
#ifdef TIMER
read_count++;
#endif
				} else {
					len = 0;
					memset(buf + (size_t)unit_size * i, 0, unit_size);
				}
			}

			if ((len > 0) && (i + 1 < source_num)){	// 最後のブロック以外なら
				// サブ・スレッドの動作状況を調べる
				j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0);
				if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){	// 計算中でないなら
					// 経過表示
					prog_num += part_num;
					if (GetTickCount() - time_last >= UPDATE_TIME){
						if (print_progress((int)((prog_num * 1000) / prog_base))){
							err = 2;
							goto error_end;
						}
						time_last = GetTickCount();
					}
					// 計算終了したブロックの次から計算を開始する
					th->off += 1;
					if (th->off > 0){	// バッファーに読み込んだ時だけ計算する
						while ((s_blk[th->off].exist != 0) &&
								((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){
							prog_num += part_num;
							th->off += 1;
#ifdef TIMER
skip_count++;
#endif
						}
					}
					th->mat = mat + th->off;
					th->now = -1;	// 初期値 - 1
					//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
					for (j = 0; j < (cpu_num + 1) / 2; j++){	// 読み込み中はスレッド数を減らす
						ResetEvent(hEnd[j]);	// リセットしておく
						SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
					}
				}
			}
		}
		if (hFile){	// 最後のソース・ファイルを閉じる
			CloseHandle(hFile);
			hFile = NULL;
		}
#ifdef TIMER
time_read += GetTickCount() - time_start;
#endif

		WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE);	// サブ・スレッドの計算終了の合図を待つ
		th->off += 1;	// 計算を開始するソース・ブロックの番号
		if (th->off > 0){	// 計算不要なソース・ブロックはとばす
			while ((s_blk[th->off].exist != 0) &&
					((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){
				prog_num += part_num;
				th->off += 1;
#ifdef TIMER
skip_count++;
#endif
			}
		}
#ifdef TIMER
		j = (th->off * 1000) / source_num;
		printf("partial decode = %d (%d.%d%%), read = %d, skip = %d\n", th->off, j / 10, j % 10, read_count, skip_count);
#endif

		// 最初の消失ブロックを探す
		recv_now = 0;	// 何番目の消失ブロックか
		for (i = 0; i < source_num; i++){
			if (s_blk[i].exist == 0){
				if (s_blk[i].size > block_off){
					if (recv_now >= part_num){	// 部分的なエンコードを施した箇所以降なら
						work_buf = p_buf;
					} else {
						work_buf = p_buf + (size_t)unit_size * recv_now;
					}
					//printf("%d: s_blk[%d].size = %d, off = %d\n", recv_now, i, s_blk[i].size, th->off);
					// 経過表示
					prog_num += source_num - th->off;
					if (GetTickCount() - time_last >= UPDATE_TIME){
						if (print_progress((int)((prog_num * 1000) / prog_base))){
							err = 2;
							goto error_end;
						}
						time_last = GetTickCount();
					}
					// 最初のブロックの復元を開始しておく
					th->mat = mat + source_num * recv_now;
					th->buf = work_buf;
					th->now = -1;	// 初期値 - 1
					//_mm_sfence();
					for (j = 0; j < cpu_num; j++){
						ResetEvent(hEnd[j]);	// リセットしておく
						SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
					}
					break;
				} else {
					//printf("%d: s_blk[%d] = empty, off = %d\n", recv_now, i, th->off);
					prog_num += source_num - th->off;
					recv_now++;	// 復元する必要が無くても番号は進める
					if (recv_now == part_num)	// 部分的なエンコードを施した箇所以降なら
						th->off = 0;	// 最初のソース・ブロックから計算する
				}
			}
		}

		// 失われたソース・ブロックごとに復元する
		last_file = -1;
		while (recv_now < block_lost){
			recv_now++;	// 次の消失ブロックの番号にする
			// ブロックの復元が終わるのを待って完成させる
			WaitForMultipleObjects(cpu_num, hEnd, TRUE, INFINITE);	// サブ・スレッドの計算終了の合図を待つ
			for (j = 1; j < cpu_num; j++){
				// 最後にサブ・スレッドのバッファーを XOR する
				galois_align_xor(sub_buf + (size_t)unit_size * (j - 1), work_buf, unit_size);
			}

			if (recv_now < block_lost){	// 最後のブロック以外なら
				// 次の消失ブロックを探す
				for (k = i + 1; k < source_num; k++){
					if (s_blk[k].exist == 0){
						if (s_blk[k].size > block_off)
							break;
						if (recv_now >= part_num)	// 部分的なエンコードを施した箇所以降なら
							th->off = 0;	// 最初のソース・ブロックから計算する
						//printf("%d: s_blk[%d] = empty, off = %d\n", recv_now, k, th->off);
						prog_num += source_num - th->off;
						recv_now++;	// 復元する必要が無くても番号は進める
					}
				}
				if (k < source_num){	// 復元するブロックが存在するなら
					if (recv_now >= part_num){	// 部分的なエンコードを施した箇所以降なら
						if (th->buf != p_buf){	// エンコードと書き込みでダブル・バッファリングする
							th->buf = p_buf;
						} else {
							th->buf += unit_size;
						}
						th->off = 0;	// 最初のソース・ブロックから計算する
					} else {
						th->buf = p_buf + (size_t)unit_size * recv_now;
					}
					//printf("%d: s_blk[%d].size = %d, off = %d\n", recv_now, k, s_blk[k].size, th->off);
					// 経過表示
					prog_num += source_num - th->off;
					if (GetTickCount() - time_last >= UPDATE_TIME){
						if (print_progress((int)((prog_num * 1000) / prog_base))){
							err = 2;
							goto error_end;
						}
						time_last = GetTickCount();
					}
					// 次のブロックの復元を開始しておく
					th->mat = mat + source_num * recv_now;
					th->now = -1;	// 初期値 - 1
					//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
					for (j = 0; j < cpu_num; j++){
						ResetEvent(hEnd[j]);	// リセットしておく
						SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
					}
				}
			}

#ifdef TIMER
time_start = GetTickCount();
#endif
			// 復元されたソース・ブロックのチェックサムを検証する
			checksum16_return(work_buf, hash, io_size);
			if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){
				printf("checksum mismatch, recovered input slice %d\n", i);
				err = 1;
				goto error_end;
			}
			// 作業用のソース・ファイルに書き込む
			if (s_blk[i].file != last_file){	// 別のファイルなら開く
				last_file = s_blk[i].file;
				if (hFile){
					CloseHandle(hFile);	// 前のファイルを閉じる
					hFile = NULL;
				}
				hFile = handle_temp_file(list_buf + files[last_file].name, file_path);
				if (hFile == INVALID_HANDLE_VALUE){
					hFile = NULL;
					err = 1;
					goto error_end;
				}
				//printf("file %d, open %S\n", last_file, file_path);
			}
			// ソース・ファイル内でのブロック断片の大きさと位置
			len = s_blk[i].size - block_off;
			if (len > io_size)
				len = io_size;
			if (file_write_data(hFile, (i - files[last_file].b_off) * (__int64)block_size + block_off, work_buf, len)){
				printf("file_write_data, input slice %d\n", i);
				err = 1;
				goto error_end;
			}
#ifdef TIMER
time_write += GetTickCount() - time_start;
write_count++;
#endif

			if (recv_now >= part_num) {	// 部分的なエンコードを施した箇所以降なら
				if (work_buf != p_buf){	// エンコードと書き込みでダブル・バッファリングする
					work_buf = p_buf;
				} else {
					work_buf += unit_size;
				}
			} else {
				work_buf = p_buf + (size_t)unit_size * recv_now;
			}
			i = k;	// 次の消失ブロックの番号
		}

		block_off += io_size;
		// 最後の作業ファイルを閉じる
		CloseHandle(hFile);
		hFile = NULL;
	}
	print_progress_done();	// 末尾ブロックの断片化によっては 100% で完了するとは限らない

#ifdef TIMER
printf("read   %d.%03d sec\n", time_read / 1000, time_read % 1000);
j = ((block_size + io_size - 1) / io_size) * block_lost;
printf("write  %d.%03d sec, count = %d/%d\n", time_write / 1000, time_write % 1000, write_count, j);
#endif

error_end:
	InterlockedExchange(&(th->now), INT_MAX / 2);	// サブ・スレッドの計算を中断する
	for (j = 0; j < cpu_num; j++){
		if (hSub[j]){	// サブ・スレッドを終了させる
			SetEvent(hRun[j]);
			WaitForSingleObject(hSub[j], INFINITE);
			CloseHandle(hSub[j]);
		}
	}
	if (hFile)
		CloseHandle(hFile);
	if (buf)
		_aligned_free(buf);
	return err;
}

int decode_method3_single(	// 復元するブロックを全て保持できる場合
	wchar_t *file_path,
	int block_lost,			// 失われたソース・ブロックの数
	HANDLE *rcv_hFile,		// リカバリ・ファイルのハンドル
	file_ctx_r *files,		// ソース・ファイルの情報
	source_ctx_r *s_blk,	// ソース・ブロックの情報
	parity_ctx_r *p_blk,	// パリティ・ブロックの情報
	unsigned short *mat)
{
	unsigned char *buf = NULL, *p_buf, *work_buf, *sub_buf, *hash;
	unsigned short *id;
	int err = 0, i, j, k, last_file, source_off, read_num, recv_now, parity_now;
	unsigned int unit_size, len;
	unsigned int time_last, prog_num = 0, prog_base;
	__int64 file_off;
	HANDLE hFile = NULL;
	HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU];
	RS_TH th[1];

	memset(hSub, 0, sizeof(HANDLE) * (MAX_CPU));
	id = mat + (block_lost * source_num);	// 何番目の消失ソース・ブロックがどのパリティで代替されるか
	unit_size = (block_size + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1);	// チェックサムの分だけ増やす

	// 作業バッファーを確保する
	read_num = read_block_num(block_lost, cpu_num - 1, 1, sse_unit);	// ソース・ブロックを何個読み込むか
	if (read_num == 0){
		//printf("cannot keep enough blocks, use another method\n");
		return -2;	// スライスを分割して処理しないと無理
	}
	//read_num = (read_num + 2) / 3 + 1;	// 実験用
	len = read_num + block_lost + (cpu_num - 1);	// 復元するブロックとサブ・スレッドの作業領域が必要
	file_off = len * (size_t)unit_size + HASH_SIZE;
	buf = _aligned_malloc((size_t)file_off, sse_unit);
	if (buf == NULL){
		printf("malloc, %I64d\n", file_off);
		err = 1;
		goto error_end;
	}
	sub_buf = buf + (size_t)unit_size * read_num;	// サブ・スレッドの作業領域
	p_buf = sub_buf + (size_t)unit_size * (cpu_num - 1);	// パリティ・ブロックを記録する領域
	hash = p_buf + (size_t)unit_size * block_lost;
	prog_base = source_num * block_lost;	// ブロックの合計掛け算個数
#ifdef TIMER
	printf("\n read some blocks, and keep all recovering blocks (single)\n");
	printf("buffer size = %I64d MB, read_num = %d, round = %d\n", file_off >> 20, read_num, (source_num + read_num - 1) / read_num);
	len = try_cache_blocking(unit_size);	// キャッシュの最適化を試みる
	printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, len, (unit_size + len - 1) / len);
	printf("prog_base = %d\n", prog_base);
#endif

	// マルチ・スレッドの準備をする
	th->buf = buf;
	th->size = block_lost;
	th->count = read_num;
	th->off = try_cache_blocking(unit_size);	// キャッシュの最適化を試みる
	for (j = 0; j < cpu_num; j++){	// サブ・スレッドごとに
		hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL);	// Auto Reset にする
		if (hRun[j] == NULL){
			print_win32_err();
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (hEnd[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		// サブ・スレッドを起動する
		th->run = hRun[j];
		th->end = hEnd[j];
		th->now = j;	// スレッド番号
		//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを起動する
		hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode3_single, (LPVOID)th, 0, NULL);
		if (hSub[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			CloseHandle(hEnd[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		WaitForSingleObject(hEnd[j], INFINITE);	// 設定終了の合図を待つ (リセットしない)
	}
	// IO が延滞しないように、サブ・スレッド一つの優先度を下げる
	SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL);

	// 何回かに別けてブロックを読み込んで、ソース・ブロックを少しずつ復元する
	print_progress_text(0, "Recovering slice");
	time_last = GetTickCount();
	wcscpy(file_path, base_dir);
	parity_now = 0;	// 何番目の代替ブロックか
	source_off = 0;	// 読み込み開始スライス番号
	while (source_off < source_num){
		if (read_num > source_num - source_off)
			read_num = source_num - source_off;
		th->buf = NULL;
		th->count = source_off;
		th->off = -1;	// まだ計算して無い印

#ifdef TIMER
read_count = 0;
time_start = GetTickCount();
#endif
		last_file = -1;
		for (i = 0; i < read_num; i++){	// スライスを一個ずつ読み込んでメモリー上に配置していく
			switch(s_blk[source_off + i].exist){
			case 0:		// バッファーにパリティ・ブロックの内容を読み込む
				if (file_read_data(rcv_hFile[p_blk[id[parity_now]].file], p_blk[id[parity_now]].off, buf + (size_t)unit_size * i, block_size)){
					printf("file_read_data, recovery slice %d\n", id[parity_now]);
					err = 1;
					goto error_end;
				}
				parity_now++;
				// パリティ・ブロックのチェックサムを計算する
				checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE);
#ifdef TIMER
read_count++;
#endif
				break;
			case 3:		// ソース・ブロックの内容は全て 0
				memset(buf + (size_t)unit_size * i, 0, unit_size);
				break;
			default:	// バッファーにソース・ブロックの内容を読み込む
				if (s_blk[source_off + i].file != last_file){	// 別のファイルなら開く
					last_file = s_blk[source_off + i].file;
					if (hFile){
						CloseHandle(hFile);	// 前のファイルを閉じる
						hFile = NULL;
					}
					if ((files[last_file].state & 7) != 0){	// 作り直した作業ファイルから読み込む
						get_temp_name(list_buf + files[last_file].name, file_path + base_len);
					} else if ((files[last_file].state & 32) != 0){	// 名前訂正失敗時には別名ファイルから読み込む
						wcscpy(file_path + base_len, list_buf + files[last_file].name2);
					} else {	// 完全なソース・ファイルから読み込む (追加訂正失敗時も)
						wcscpy(file_path + base_len, list_buf + files[last_file].name);
					}
					hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
					if (hFile == INVALID_HANDLE_VALUE){
						print_win32_err();
						hFile = NULL;
						printf_cp("cannot open file, %s\n", file_path);
						err = 1;
						goto error_end;
					}
				}
				len = s_blk[source_off + i].size;
				file_off = (source_off + i - files[last_file].b_off) * (__int64)block_size;
				if (file_read_data(hFile, file_off, buf + (size_t)unit_size * i, len)){
					printf("file_read_data, input slice %d\n", source_off + i);
					err = 1;
					goto error_end;
				}
				if (len < block_size)
					memset(buf + ((size_t)unit_size * i + len), 0, block_size - len);
				// ソース・ブロックのチェックサムを計算する
				checksum16_altmap(buf + (size_t)unit_size * i, buf + ((size_t)unit_size * i + unit_size - HASH_SIZE), unit_size - HASH_SIZE);
#ifdef TIMER
read_count++;
#endif
			}

			if (i + 1 < read_num){	// 最後のブロック以外なら
				// サブ・スレッドの動作状況を調べる
				j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0);
				if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){	// 計算中でないなら
					// 経過表示
					prog_num += block_lost;
					if (GetTickCount() - time_last >= UPDATE_TIME){
						if (print_progress(((__int64)prog_num * 1000) / prog_base)){
							err = 2;
							goto error_end;
						}
						time_last = GetTickCount();
					}
					// 計算終了したブロックの次から計算を開始する
					th->off += 1;
					th->mat = mat + (th->off + source_off);
					th->now = -1;	// 初期値 - 1
					//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
					for (j = 0; j < (cpu_num + 1) / 2; j++){	// 読み込み中はスレッド数を減らす
						ResetEvent(hEnd[j]);	// リセットしておく
						SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
					}
				}
			}
		}
		if (hFile){	// 最後のソース・ファイルを閉じる
			CloseHandle(hFile);
			hFile = NULL;
		}
#ifdef TIMER
time_read += GetTickCount() - time_start;
#endif

		WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE);	// サブ・スレッドの計算終了の合図を待つ
		th->off += 1;	// 計算を開始するソース・ブロックの番号
		if (source_off + th->off == 0)	// エラーや実験時以外は th->off は 0 にならない
			memset(p_buf, 0, (size_t)unit_size * block_lost);
#ifdef TIMER
		j = (th->off * 1000) / read_num;
		printf("partial decode = %d (%d.%d%%), read = %d\n", th->off, j / 10, j % 10, read_count);
#endif

		// 最初の消失ブロックを探す
		recv_now = 0;	// 何番目の消失ブロックか
		for (i = 0; i < source_num; i++){
			if (s_blk[i].exist == 0){
				// 経過表示
				prog_num += read_num - th->off;
				if (GetTickCount() - time_last >= UPDATE_TIME){
					if (print_progress(((__int64)prog_num * 1000) / prog_base)){
						err = 2;
						goto error_end;
					}
					time_last = GetTickCount();
				}
				// 最初のブロックの復元を開始しておく
				//printf("%d: s_blk[%d].size = %d\n", recv_now, i, s_blk[i].size);
				work_buf = p_buf;
				th->count = read_num;
				th->mat = mat + source_off;
				th->buf = work_buf;
				th->now = -1;	// 初期値 - 1
				//_mm_sfence();
				for (j = 0; j < cpu_num; j++){
					ResetEvent(hEnd[j]);	// リセットしておく
					SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
				}
				break;
			}
		}

		// 失われたソース・ブロックごとに復元する
		last_file = -1;
		while (recv_now < block_lost){
			recv_now++;	// 次の消失ブロックの番号にする
			// ブロックの復元が終わるのを待って完成させる
			WaitForMultipleObjects(cpu_num, hEnd, TRUE, INFINITE);	// サブ・スレッドの計算終了の合図を待つ
			for (j = 1; j < cpu_num; j++){
				// 最後にサブ・スレッドのバッファーを XOR する
				galois_align_xor(sub_buf + (size_t)unit_size * (j - 1), work_buf, unit_size);
			}

/*
#ifdef TIMER
			if (source_off + read_num < source_num){	// 実験用
				// 復元されたソース・ブロックのチェックサムを検証する
				checksum16_return(work_buf, hash, unit_size - HASH_SIZE);
				if (memcmp(work_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){
					printf("checksum mismatch, temporary recovered input slice %d (%d)\n", i, recv_now);
					err = 1;
					goto error_end;
				}
				// 並び変えなおす
				checksum16_altmap(work_buf, work_buf + unit_size - HASH_SIZE, unit_size - HASH_SIZE);
			}
#endif
*/

			if (recv_now < block_lost){	// 最後のブロック以外なら
				// 次の消失ブロックを探す
				for (k = i + 1; k < source_num; k++){
					if (s_blk[k].exist == 0)
						break;	// 復元するブロックは必ず存在するはず
				}
				// 経過表示
				prog_num += read_num - th->off;
				if (GetTickCount() - time_last >= UPDATE_TIME){
					if (print_progress(((__int64)prog_num * 1000) / prog_base)){
						err = 2;
						goto error_end;
					}
					time_last = GetTickCount();
				}
				// 次のブロックの復元を開始しておく
				//printf("%d: s_blk[%d].size = %d\n", recv_now, k, s_blk[k].size);
				th->mat = mat + (source_num * recv_now + source_off);	// 読み込んだソース・ブロックの位置までずらす
				th->buf += unit_size;
				th->now = -1;	// 初期値 - 1
				//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
				for (j = 0; j < cpu_num; j++){
					ResetEvent(hEnd[j]);	// リセットしておく
					SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
				}
			}

			if (source_off + read_num >= source_num){	// 最後の書き込みなら
#ifdef TIMER
time_start = GetTickCount();
#endif
				// 復元されたソース・ブロックのチェックサムを検証する
				checksum16_return(work_buf, hash, unit_size - HASH_SIZE);
				if (memcmp(work_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){
					printf("checksum mismatch, recovered input slice %d\n", i);
					err = 1;
					goto error_end;
				}
				// 作業用のソース・ファイルに書き込む
				if (s_blk[i].file != last_file){	// 別のファイルなら開く
					last_file = s_blk[i].file;
					if (hFile){
						CloseHandle(hFile);	// 前のファイルを閉じる
						hFile = NULL;
					}
					hFile = handle_temp_file(list_buf + files[last_file].name, file_path);
					if (hFile == INVALID_HANDLE_VALUE){
						hFile = NULL;
						err = 1;
						goto error_end;
					}
					//printf("file %d, open %S\n", last_file, file_path);
				}
				// ソース・ファイル内でのブロックの大きさと位置
				if (file_write_data(hFile, (i - files[last_file].b_off) * (__int64)block_size, work_buf, s_blk[i].size)){
					printf("file_write_data, input slice %d\n", i);
					err = 1;
					goto error_end;
				}
#ifdef TIMER
time_write += GetTickCount() - time_start;
#endif
			}

			work_buf += unit_size;
			i = k;	// 次の消失ブロックの番号
		}

		source_off += read_num;
		// 最後の作業ファイルを閉じる
		CloseHandle(hFile);
		hFile = NULL;
	}
	print_progress_done();	// 末尾ブロックの断片化によっては 100% で完了するとは限らない

#ifdef TIMER
printf("read   %d.%03d sec\n", time_read / 1000, time_read % 1000);
printf("write  %d.%03d sec\n", time_write / 1000, time_write % 1000);
#endif

error_end:
	InterlockedExchange(&(th->now), INT_MAX / 2);	// サブ・スレッドの計算を中断する
	for (j = 0; j < cpu_num; j++){
		if (hSub[j]){	// サブ・スレッドを終了させる
			SetEvent(hRun[j]);
			WaitForSingleObject(hSub[j], INFINITE);
			CloseHandle(hSub[j]);
		}
	}
	if (hFile)
		CloseHandle(hFile);
	if (buf)
		_aligned_free(buf);
	return err;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// chunk ごとに並び替えるバージョン

// chunk ごとに並び替えて計算するためのスレッド
static DWORD WINAPI thread_decode2_arrange(LPVOID lpParameter)
{
	unsigned char *s_buf, *p_buf, *work_buf, *c_buf;
	unsigned short *factor, *factor2;
	int i, j, src_start, src_num, max_num;
	int chunk_num, part_start, part_num, cover_num;
	unsigned int unit_size, len, off, chunk_size;
	size_t chunk_range;
	HANDLE hRun, hEnd;
	RS_TH *th;
#ifdef TIMER
unsigned int loop_count2a = 0, loop_count2b = 0;
unsigned int time_start2, time_encode2a = 0, time_encode2b = 0;
#endif

	th = (RS_TH *)lpParameter;
	s_buf = th->buf;
	unit_size = th->size;
	chunk_size = th->off;
	part_num = th->count;
	hRun = th->run;
	hEnd = th->end;
	//_mm_sfence();
	SetEvent(hEnd);	// 設定完了を通知する

	chunk_range = (size_t)chunk_size * source_num;
	chunk_num = (unit_size + chunk_size - 1) / chunk_size;
	p_buf = s_buf + (size_t)unit_size * (source_num + 1);

	WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	while (th->now < INT_MAX / 2){
#ifdef TIMER
time_start2 = GetTickCount();
#endif
		src_start = th->off;	// ソース・ブロック番号
		factor = th->mat;
		len = chunk_size;

		if (th->size == 0){	// ソース・ブロック読み込み中
			max_num = chunk_num * part_num;
			// パリティ・ブロックごとに掛け算して追加していく
			while ((j = InterlockedIncrement(&(th->now))) < max_num){	// j = ++th_now
				off = j / part_num;	// chunk の番号
				j = j % part_num;	// lost block の番号
				c_buf = s_buf + chunk_range * off;
				off *= chunk_size;
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				c_buf += (size_t)len * src_start;
				if (src_start == 0)	// 最初のブロックを計算する際に
					memset(p_buf + ((size_t)unit_size * j + off), 0, len);	// ブロックを 0で埋める
				galois_align_multiply(c_buf, p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]);
#ifdef TIMER
loop_count2a++;
#endif
			}
#ifdef TIMER
time_encode2a += GetTickCount() - time_start2;
#endif
		} else {	// 消失ブロックを部分的に保持する場合
			// スレッドごとに復元する消失ブロックの chunk を変える
			src_num = source_num - th->off;
			cover_num = th->size;
			part_start = th->count;
			max_num = chunk_num * cover_num;
			while ((j = InterlockedIncrement(&(th->now))) < max_num){	// j = ++th_now
				off = j / cover_num;	// chunk の番号
				j = j % cover_num;		// lost block の番号
				c_buf = s_buf + chunk_range * off;	// source chunk の位置
				off *= chunk_size;		// parity chunk の位置
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				c_buf += (size_t)len * src_start;
				work_buf = p_buf + (size_t)unit_size * j + off;
				if (part_start != 0)
					memset(work_buf, 0, len);	// 最初の part_num 以降は 2nd encode だけなので 0で埋める
				factor2 = factor + source_num * (part_start + j);

				// ソース・ブロックごとにパリティを追加していく
				for (i = 0; i < src_num; i++)
					galois_align_multiply(c_buf + ((size_t)len * i), work_buf, len, factor2[i]);
#ifdef TIMER
loop_count2b += src_num;
#endif
			}
#ifdef TIMER
time_encode2b += GetTickCount() - time_start2;
#endif
		}
		//_mm_sfence();	// メモリーへの書き込みを完了する
		SetEvent(hEnd);	// 計算終了を通知する
		WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	}
#ifdef TIMER
loop_count2a /= chunk_num;	// chunk数で割ってブロック数にする
loop_count2b /= chunk_num;
printf("sub-thread : total loop = %d\n", loop_count2a + loop_count2b);
if (time_encode2a > 0){
	i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072));
} else {
	i = 0;
}
if (loop_count2a > 0)
	printf(" 1st decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i);
if (time_encode2b > 0){
	i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072));
} else {
	i = 0;
}
printf(" 2nd decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i);
#endif

	// 終了処理
	CloseHandle(hRun);
	CloseHandle(hEnd);
	return 0;
}

static DWORD WINAPI thread_decode3_arrange(LPVOID lpParameter)
{
	unsigned char *s_buf, *p_buf, *work_buf, *c_buf;
	unsigned short *factor, *factor2;
	int i, j, block_lost, src_start, src_num, max_num;
	int chunk_num, source_off, read_num;
	unsigned int unit_size, len, off, chunk_size;
	size_t chunk_range;
	HANDLE hRun, hEnd;
	RS_TH *th;
#ifdef TIMER
unsigned int loop_count2a = 0, loop_count2b = 0;
unsigned int time_start2, time_encode2a = 0, time_encode2b = 0;
#endif

	th = (RS_TH *)lpParameter;
	s_buf = th->buf;
	unit_size = th->size;
	chunk_size = th->off;
	read_num = th->count;
	block_lost = th->now;
	hRun = th->run;
	hEnd = th->end;
	//_mm_sfence();
	SetEvent(hEnd);	// 設定完了を通知する

	chunk_num = (unit_size + chunk_size - 1) / chunk_size;
	max_num = chunk_num * block_lost;
	p_buf = s_buf + (size_t)unit_size * (read_num + 1);

	WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	while (th->now < INT_MAX / 2){
#ifdef TIMER
time_start2 = GetTickCount();
#endif
		source_off = (th->count) >> 16;
		read_num = (th->count) & 0xFFFF;
		src_start = th->off;	// ソース・ブロック番号
		factor = th->mat;
		len = chunk_size;
		chunk_range = (size_t)chunk_size * read_num;

		if (th->size == 0){	// ソース・ブロック読み込み中
			// パリティ・ブロックごとに掛け算して追加していく
			while ((j = InterlockedIncrement(&(th->now))) < max_num){	// j = ++th_now
				off = j / block_lost;	// chunk の番号
				j = j % block_lost;		// lost block の番号
				c_buf = s_buf + chunk_range * off;
				off *= chunk_size;
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				c_buf += (size_t)len * (src_start - source_off);
				if (src_start == 0)	// 最初のブロックを計算する際に
					memset(p_buf + ((size_t)unit_size * j + off), 0, len);	// ブロックを 0で埋める
				galois_align_multiply(c_buf, p_buf + ((size_t)unit_size * j + off), len, factor[source_num * j]);
#ifdef TIMER
loop_count2a++;
#endif
			}
#ifdef TIMER
time_encode2a += GetTickCount() - time_start2;
#endif
		} else {	// 全ての消失ブロックを保持する場合
			// スレッドごとに復元する消失ブロックの chunk を変える
			src_num = th->size;
			while ((j = InterlockedIncrement(&(th->now))) < max_num){	// j = ++th_now
				off = j / block_lost;	// chunk の番号
				j = j % block_lost;		// lost block の番号
				c_buf = s_buf + chunk_range * off;	// source chunk の位置
				off *= chunk_size;		// parity chunk の位置
				if (off + len > unit_size)
					len = unit_size - off;	// 最後の chunk だけサイズが異なるかも
				c_buf += (size_t)len * (src_start - source_off);
				work_buf = p_buf + (size_t)unit_size * j + off;
				factor2 = factor + source_num * j;

				// ソース・ブロックごとにパリティを追加していく
				for (i = 0; i < src_num; i++)
					galois_align_multiply(c_buf + ((size_t)len * i), work_buf, len, factor2[i]);
#ifdef TIMER
loop_count2b += src_num;
#endif
			}
#ifdef TIMER
time_encode2b += GetTickCount() - time_start2;
#endif
		}
		//_mm_sfence();	// メモリーへの書き込みを完了する
		SetEvent(hEnd);	// 計算終了を通知する
		WaitForSingleObject(hRun, INFINITE);	// 計算開始の合図を待つ
	}
#ifdef TIMER
loop_count2a /= chunk_num;	// chunk数で割ってブロック数にする
loop_count2b /= chunk_num;
printf("sub-thread : total loop = %d\n", loop_count2a + loop_count2b);
if (time_encode2a > 0){
	i = (int)((__int64)loop_count2a * unit_size * 125 / ((__int64)time_encode2a * 131072));
} else {
	i = 0;
}
if (loop_count2a > 0)
	printf(" 1st decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2a / 1000, time_encode2a % 1000, loop_count2a, i);
if (time_encode2b > 0){
	i = (int)((__int64)loop_count2b * unit_size * 125 / ((__int64)time_encode2b * 131072));
} else {
	i = 0;
}
printf(" 2nd decode %d.%03d sec, %d loop, %d MB/s\n", time_encode2b / 1000, time_encode2b % 1000, loop_count2b, i);
#endif

	// 終了処理
	CloseHandle(hRun);
	CloseHandle(hEnd);
	return 0;
}


int decode_method2_arrange(	// ソース・データを全て読み込む場合 (chunkごとに並び替える)
	wchar_t *file_path,
	int block_lost,			// 失われたソース・ブロックの数
	HANDLE *rcv_hFile,		// リカバリ・ファイルのハンドル
	file_ctx_r *files,		// ソース・ファイルの情報
	source_ctx_r *s_blk,	// ソース・ブロックの情報
	parity_ctx_r *p_blk,		// パリティ・ブロックの情報
	unsigned short *mat)
{
	unsigned char *buf = NULL, *p_buf, *work_buf, *read_buf, *hash;
	unsigned short *id;
	int err = 0, i, j, last_file, part_start, part_num, recv_now;
	int src_num, chunk_num, cover_num;
	unsigned int io_size, unit_size, len, block_off, chunk_size;
	unsigned int time_last, prog_write;
	__int64 file_off, prog_num = 0, prog_base;
	HANDLE hFile = NULL;
	HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU];
	RS_TH th[1];

	memset(hSub, 0, sizeof(HANDLE) * MAX_CPU);
	id = mat + (block_lost * source_num);	// 何番目の消失ソース・ブロックがどのパリティで代替されるか

	// 作業バッファーを確保する（読み込み用に1個多くする）
	part_num = source_num >> PART_MAX_RATE;	// ソース・ブロック数に対する割合で最大量を決める
	//part_num = (block_lost + 2) / 3;	// 確保量の実験用
	if (part_num < block_lost){	// 分割して計算するなら
		i = (block_lost + part_num - 1) / part_num;	// 分割回数
		part_num = (block_lost + i - 1) / i;
		part_num = ((part_num + cpu_num - 1) / cpu_num) * cpu_num;	// cpu_num の倍数にする（切り上げ）
	}
	if (part_num > block_lost)
		part_num = block_lost;
	io_size = get_io_size(source_num, &part_num, 1, sse_unit);
	//io_size = (((io_size + 1) / 2 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE;	// 2分割の実験用
	//io_size = (((io_size + 2) / 3 + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1)) - HASH_SIZE;	// 3分割の実験用
	unit_size = io_size + HASH_SIZE;	// チェックサムの分だけ増やす
	file_off = (source_num + 1 + part_num) * (size_t)unit_size + HASH_SIZE;
	buf = _aligned_malloc((size_t)file_off, sse_unit);
	if (buf == NULL){
		printf("malloc, %I64d\n", file_off);
		err = 1;
		goto error_end;
	}
	read_buf = buf + (size_t)unit_size * source_num;
	p_buf = read_buf + unit_size;	// 復元したブロックを記録する領域
	hash = p_buf + (size_t)unit_size * part_num;
	prog_base = (block_size + io_size - 1) / io_size;
	prog_write = source_num >> 5;	// 計算で 97%、書き込みで 3% ぐらい
	if (prog_write == 0)
		prog_write = 1;
	prog_base *= (__int64)(source_num + prog_write) * block_lost;	// 全体の断片の個数
	chunk_size = try_cache_blocking(unit_size);
	//chunk_size = ((chunk_size + 2) / 3 + (sse_unit - 1)) & ~(sse_unit - 1);	// 1/3の実験用
	chunk_num = (unit_size + chunk_size - 1) / chunk_size;
#ifdef TIMER
	printf("\n read all blocks, and keep some recovering blocks (arrange)\n");
	printf("buffer size = %I64d MB, io_size = %d, split = %d\n", file_off >> 20, io_size, (block_size + io_size - 1) / io_size);
	printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, chunk_size, chunk_num);
	printf("prog_base = %I64d, unit_size = %d\n", prog_base, unit_size);
#endif

	// マルチ・スレッドの準備をする
	th->buf = buf;
	th->size = unit_size;
	th->count = part_num;
	th->off = chunk_size;	// キャッシュの最適化を試みる
	for (j = 0; j < cpu_num; j++){	// サブ・スレッドごとに
		hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL);	// Auto Reset にする
		if (hRun[j] == NULL){
			print_win32_err();
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (hEnd[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		// サブ・スレッドを起動する
		th->run = hRun[j];
		th->end = hEnd[j];
		//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを起動する
		hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode2_arrange, (LPVOID)th, 0, NULL);
		if (hSub[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			CloseHandle(hEnd[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		WaitForSingleObject(hEnd[j], INFINITE);	// 設定終了の合図を待つ (リセットしない)
	}
	// IO が延滞しないように、サブ・スレッド一つの優先度を下げる
	SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL);

	// ブロック断片を読み込んで、消失ブロック断片を復元する
	print_progress_text(0, "Recovering slice");
	time_last = GetTickCount();
	wcscpy(file_path, base_dir);
	block_off = 0;
	while (block_off < block_size){
		th->size = 0;	// 1st decode
		th->off = -1;	// まだ計算して無い印

#ifdef TIMER
read_count = 0;
skip_count = 0;
time_start = GetTickCount();
#endif
		last_file = -1;
		recv_now = 0;	// 何番目の代替ブロックか
		for (i = 0; i < source_num; i++){
			switch(s_blk[i].exist){
			case 0:		// バッファーにパリティ・ブロックの内容を読み込む
				len = block_size - block_off;
				if (len > io_size)
					len = io_size;
				file_off = p_blk[id[recv_now]].off + (__int64)block_off;
				if (file_read_data(rcv_hFile[p_blk[id[recv_now]].file], file_off, read_buf, len)){
					printf("file_read_data, recovery slice %d\n", id[recv_now]);
					err = 1;
					goto error_end;
				}
				if (len < io_size)
					memset(read_buf + len, 0, io_size - len);
				recv_now++;
				// パリティ・ブロックのチェックサムを計算する
				checksum16_altmap(read_buf, read_buf + io_size, io_size);
				// chunk ごとに並び替える
				arrange_copy(buf, read_buf, unit_size, i, source_num, chunk_num, chunk_size);
#ifdef TIMER
read_count++;
#endif
				break;
			case 3:		// ソース・ブロックの内容は全て 0
				len = 0;
				arrange_zero(buf, unit_size, i, source_num, chunk_num, chunk_size);	// 0 で埋める所も chunk ごとにする
				break;
			default:	// バッファーにソース・ブロックの内容を読み込む
				if (s_blk[i].file != last_file){	// 別のファイルなら開く
					last_file = s_blk[i].file;
					if (hFile){
						CloseHandle(hFile);	// 前のファイルを閉じる
						hFile = NULL;
					}
					if ((files[last_file].state & 7) != 0){	// 作り直した作業ファイルから読み込む
						get_temp_name(list_buf + files[last_file].name, file_path + base_len);
					} else if ((files[last_file].state & 32) != 0){	// 名前訂正失敗時には別名ファイルから読み込む
						wcscpy(file_path + base_len, list_buf + files[last_file].name2);
					} else {	// 完全なソース・ファイルから読み込む
						wcscpy(file_path + base_len, list_buf + files[last_file].name);
					}
					hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
					if (hFile == INVALID_HANDLE_VALUE){
						print_win32_err();
						hFile = NULL;
						printf_cp("cannot open file, %s\n", file_path);
						err = 1;
						goto error_end;
					}
				}
				if (s_blk[i].size > block_off){
					len = s_blk[i].size - block_off;
					if (len > io_size)
						len = io_size;
					file_off = (i - files[last_file].b_off) * (__int64)block_size + (__int64)block_off;
					if (file_read_data(hFile, file_off, read_buf, len)){
						printf("file_read_data, input slice %d\n", i);
						err = 1;
						goto error_end;
					}
					if (len < io_size)
						memset(read_buf + len, 0, io_size - len);
					// ソース・ブロックのチェックサムを計算する
					checksum16_altmap(read_buf, read_buf + io_size, io_size);
					// chunk ごとに並び替える
					arrange_copy(buf, read_buf, unit_size, i, source_num, chunk_num, chunk_size);
#ifdef TIMER
read_count++;
#endif
				} else {
					len = 0;
					arrange_zero(buf, unit_size, i, source_num, chunk_num, chunk_size);	// 0 で埋める所も chunk ごとにする
				}
			}

			if ((len > 0) && (i + 1 < source_num)){	// 最後のブロック以外なら
				// サブ・スレッドの動作状況を調べる
				j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0);
				if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){	// 計算中でないなら
					// 経過表示
					prog_num += part_num;
					if (GetTickCount() - time_last >= UPDATE_TIME){
						if (print_progress((int)((prog_num * 1000) / prog_base))){
							err = 2;
							goto error_end;
						}
						time_last = GetTickCount();
					}
					// 計算終了したブロックの次から計算を開始する
					th->off += 1;
					if (th->off > 0){	// バッファーに読み込んだ時だけ計算する
						while ((s_blk[th->off].exist != 0) &&
								((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){
							prog_num += part_num;
							th->off += 1;
#ifdef TIMER
skip_count++;
#endif
						}
					}
					th->mat = mat + th->off;
					th->now = -1;	// 初期値 - 1
					//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
					for (j = 0; j < (cpu_num + 1) / 2; j++){
						ResetEvent(hEnd[j]);	// リセットしておく
						SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
					}
				}
			}
		}
		if (hFile){	// 最後のソース・ファイルを閉じる
			CloseHandle(hFile);
			hFile = NULL;
		}
#ifdef TIMER
time_read += GetTickCount() - time_start;
#endif

		WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE);	// サブ・スレッドの計算終了の合図を待つ
		th->off += 1;	// 計算を開始するソース・ブロックの番号
		if (th->off > 0){	// 計算不要なソース・ブロックはとばす
			while ((s_blk[th->off].exist != 0) &&
					((s_blk[th->off].size <= block_off) || (s_blk[th->off].exist == 3))){
				prog_num += part_num;
				th->off += 1;
#ifdef TIMER
skip_count++;
#endif
			}
		} else {	// エラーや実験時以外は th->off は 0 にならない
			memset(p_buf, 0, (size_t)unit_size * part_num);
		}
#ifdef TIMER
		j = (th->off * 1000) / source_num;
		printf("partial decode = %d (%d.%d%%), read = %d, skip = %d\n", th->off, j / 10, j % 10, read_count, skip_count);
#endif
		recv_now = -1;	// 消失ブロックの本来のソース番号
		last_file = -1;

		// cover_num ごとに処理する
		part_start = 0;
		cover_num = part_num;	// part_num は cpu_num の倍数にすること
		src_num = source_num - th->off;	// 一度に処理する量 (src_num > 0)
		th->buf = buf + (size_t)unit_size * (th->off);
		while (part_start < block_lost){
			if (part_start == part_num){	// part_num 分の計算が終わったら
				th->off = 0;	// 最初の計算以降は全てのソース・ブロックを対象にする
				src_num = source_num;	// source_num - th->off
				th->buf = buf;	// buf + (size_t)unit_size * (th->off);
			}
			if (part_start + cover_num > block_lost)
				cover_num = block_lost - part_start;
			//printf("part_start = %d, src_num = %d / %d, cover_num = %d\n", part_start, src_num, source_num, cover_num);

			// スレッドごとに消失ブロックを計算する
			th->mat = mat + (th->off);
			th->size = cover_num;
			th->count = part_start;
			th->now = -1;	// 初期値 - 1
			//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
			for (j = 0; j < cpu_num; j++){
				ResetEvent(hEnd[j]);	// リセットしておく
				SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
			}

			// サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待つ
			while (WaitForMultipleObjects(cpu_num, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){
				// th-now が最高値なので、計算が終わってるのは th-now - cpu_num 個となる
				j = th->now - cpu_num;
				if (j < 0)
					j = 0;
				j /= chunk_num;	// chunk数で割ってブロック数にする
				// 経過表示（UPDATE_TIME 時間待った場合なので、必ず経過してるはず）
				if (print_progress((int)(((prog_num + src_num * j) * 1000) / prog_base))){
					err = 2;
					goto error_end;
				}
				time_last = GetTickCount();
			}
			prog_num += src_num * cover_num;

#ifdef TIMER
time_start = GetTickCount();
#endif
			// 復元されたブロックを書き込む
			work_buf = p_buf;
			for (i = part_start; i < part_start + cover_num; i++){
				for (j = recv_now + 1; j < source_num; j++){	// 何番のソース・ブロックか
					if (s_blk[j].exist == 0){
						recv_now = j;
						break;
					}
				}
				//printf(" lost block[%d] = source block[%d]\n", i, recv_now);

				// 復元されたソース・ブロックのチェックサムを検証する
				checksum16_return(work_buf, hash, io_size);
				if (memcmp(work_buf + io_size, hash, HASH_SIZE) != 0){
					printf("checksum mismatch, recovered input slice %d\n", recv_now);
					err = 1;
					goto error_end;
				}
				if (s_blk[recv_now].size <= block_off){	// 書き込み不要
					work_buf += unit_size;
					prog_num += prog_write;
					continue;
				}
				// 作業用のソース・ファイルに書き込む
				if (s_blk[recv_now].file != last_file){	// 別のファイルなら開く
					last_file = s_blk[recv_now].file;
					if (hFile){
						CloseHandle(hFile);	// 前のファイルを閉じる
						hFile = NULL;
					}
					hFile = handle_temp_file(list_buf + files[last_file].name, file_path);
					if (hFile == INVALID_HANDLE_VALUE){
						hFile = NULL;
						err = 1;
						goto error_end;
					}
					//printf("file %d, open %S\n", last_file, file_path);
				}
				// ソース・ファイル内でのブロック断片の大きさと位置
				len = s_blk[recv_now].size - block_off;
				if (len > io_size)
					len = io_size;
				if (file_write_data(hFile, (recv_now - files[last_file].b_off) * (__int64)block_size + block_off, work_buf, len)){
					printf("file_write_data, input slice %d\n", recv_now);
					err = 1;
					goto error_end;
				}
#ifdef TIMER
write_count++;
#endif
				work_buf += unit_size;

				// 経過表示
				prog_num += prog_write;
				if (GetTickCount() - time_last >= UPDATE_TIME){
					if (print_progress((int)((prog_num * 1000) / prog_base))){
						err = 2;
						goto error_end;
					}
					time_last = GetTickCount();
				}
			}
#ifdef TIMER
time_write += GetTickCount() - time_start;
#endif

			part_start += part_num;	// 次の消失ブロック位置にする
		}

		block_off += io_size;
		// 最後の作業ファイルを閉じる
		CloseHandle(hFile);
		hFile = NULL;
	}
	print_progress_done();
	//printf("prog_num = %I64d / %I64d\n", prog_num, prog_base);

#ifdef TIMER
printf("read   %d.%03d sec\n", time_read / 1000, time_read % 1000);
j = ((block_size + io_size - 1) / io_size) * block_lost;
printf("write  %d.%03d sec, count = %d/%d\n", time_write / 1000, time_write % 1000, write_count, j);
#endif

error_end:
	InterlockedExchange(&(th->now), INT_MAX / 2);	// サブ・スレッドの計算を中断する
	for (j = 0; j < cpu_num; j++){
		if (hSub[j]){	// サブ・スレッドを終了させる
			SetEvent(hRun[j]);
			WaitForSingleObject(hSub[j], INFINITE);
			CloseHandle(hSub[j]);
		}
	}
	if (hFile)
		CloseHandle(hFile);
	if (buf)
		_aligned_free(buf);
	return err;
}

int decode_method3_arrange(	// 復元するブロックを全て保持できる場合 (chunkごとに並び替える)
	wchar_t *file_path,
	int block_lost,			// 失われたソース・ブロックの数
	HANDLE *rcv_hFile,		// リカバリ・ファイルのハンドル
	file_ctx_r *files,		// ソース・ファイルの情報
	source_ctx_r *s_blk,	// ソース・ブロックの情報
	parity_ctx_r *p_blk,	// パリティ・ブロックの情報
	unsigned short *mat)
{
	unsigned char *buf = NULL, *p_buf, *work_buf, *read_buf, *hash;
	unsigned short *id;
	int err = 0, i, j, last_file, source_off, read_num, recv_now, parity_now;
	int src_num, chunk_num;
	unsigned int unit_size, len, chunk_size;
	unsigned int time_last, prog_write;
	__int64 file_off, prog_num = 0, prog_base;
	HANDLE hFile = NULL;
	HANDLE hSub[MAX_CPU], hRun[MAX_CPU], hEnd[MAX_CPU];
	RS_TH th[1];

	memset(hSub, 0, sizeof(HANDLE) * MAX_CPU);
	id = mat + (block_lost * source_num);	// 何番目の消失ソース・ブロックがどのパリティで代替されるか
	unit_size = (block_size + HASH_SIZE + (sse_unit - 1)) & ~(sse_unit - 1);	// チェックサムの分だけ増やす

	// 作業バッファーを確保する（読み込み用に1個多くする）
	read_num = read_block_num(block_lost, 1, 1, sse_unit);	// ソース・ブロックを何個読み込むか
	if (read_num == 0){
		//printf("cannot keep enough blocks, use another method\n");
		return -2;	// スライスを分割して処理しないと無理
	}
	//read_num = (read_num + 1) / 2 + 1;	// 2分割の実験用
	//read_num = (read_num + 2) / 3 + 1;	// 3分割の実験用
	file_off = (read_num + 1 + block_lost) * (size_t)unit_size + HASH_SIZE;
	buf = _aligned_malloc((size_t)file_off, sse_unit);
	if (buf == NULL){
		printf("malloc, %I64d\n", file_off);
		err = 1;
		goto error_end;
	}
	read_buf = buf + (size_t)unit_size * read_num;
	p_buf = read_buf + unit_size;	// パリティ・ブロックを記録する領域
	hash = p_buf + (size_t)unit_size * block_lost;
	prog_write = source_num >> 5;	// 計算で 97%、書き込みで 3% ぐらい
	if (prog_write == 0)
		prog_write = 1;
	prog_base = (__int64)(source_num + prog_write) * block_lost;	// ブロックの合計掛け算個数 + 書き込み回数
	chunk_size = try_cache_blocking(unit_size);
	chunk_num = (unit_size + chunk_size - 1) / chunk_size;
#ifdef TIMER
	printf("\n read some blocks, and keep all recovering blocks (chunk)\n");
	printf("buffer size = %I64d MB, read_num = %d, round = %d\n", file_off >> 20, read_num, (source_num + read_num - 1) / read_num);
	printf("cache: limit size = %d, chunk_size = %d, split = %d\n", cpu_cache & 0x7FFF8000, chunk_size, chunk_num);
	printf("prog_base = %I64d, unit_size = %d\n", prog_base, unit_size);
#endif

	// マルチ・スレッドの準備をする
	th->buf = buf;
	th->size = unit_size;
	th->count = read_num;
	th->off = chunk_size;	// キャッシュの最適化を試みる
	th->now = block_lost;
	for (j = 0; j < cpu_num; j++){	// サブ・スレッドごとに
		hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL);	// Auto Reset にする
		if (hRun[j] == NULL){
			print_win32_err();
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		hEnd[j] = CreateEvent(NULL, TRUE, FALSE, NULL);
		if (hEnd[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		// サブ・スレッドを起動する
		th->run = hRun[j];
		th->end = hEnd[j];
		//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを起動する
		hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_decode3_arrange, (LPVOID)th, 0, NULL);
		if (hSub[j] == NULL){
			print_win32_err();
			CloseHandle(hRun[j]);
			CloseHandle(hEnd[j]);
			printf("error, sub-thread\n");
			err = 1;
			goto error_end;
		}
		WaitForSingleObject(hEnd[j], INFINITE);	// 設定終了の合図を待つ (リセットしない)
	}
	// IO が延滞しないように、サブ・スレッド一つの優先度を下げる
	SetThreadPriority(hSub[0], THREAD_PRIORITY_BELOW_NORMAL);

	// 何回かに別けてブロックを読み込んで、消失ブロックを少しずつ復元する
	print_progress_text(0, "Recovering slice");
	time_last = GetTickCount();
	wcscpy(file_path, base_dir);
	parity_now = 0;	// 何番目の代替ブロックか
	source_off = 0;	// 読み込み開始スライス番号
	while (source_off < source_num){
		if (read_num > source_num - source_off)
			read_num = source_num - source_off;
		th->size = 0;	// 1st decode
		th->count = (source_off << 16) | read_num;
		th->off = source_off - 1;	// まだ計算して無い印

#ifdef TIMER
read_count = 0;
time_start = GetTickCount();
#endif
		last_file = -1;
		for (i = 0; i < read_num; i++){	// スライスを一個ずつ読み込んでメモリー上に配置していく
			switch(s_blk[source_off + i].exist){
			case 0:		// バッファーにパリティ・ブロックの内容を読み込む
				if (file_read_data(rcv_hFile[p_blk[id[parity_now]].file], p_blk[id[parity_now]].off, read_buf, block_size)){
					printf("file_read_data, recovery slice %d\n", id[parity_now]);
					err = 1;
					goto error_end;
				}
				parity_now++;
				// パリティ・ブロックのチェックサムを計算する
				checksum16_altmap(read_buf, read_buf + unit_size - HASH_SIZE, unit_size - HASH_SIZE);
				// chunk ごとに並び替える
				arrange_copy(buf, read_buf, unit_size, i, read_num, chunk_num, chunk_size);
#ifdef TIMER
read_count++;
#endif
				break;
			case 3:		// ソース・ブロックの内容は全て 0
				arrange_zero(buf, unit_size, i, read_num, chunk_num, chunk_size);	// 0 で埋める所も chunk ごとにする
				break;
			default:	// バッファーにソース・ブロックの内容を読み込む
				if (s_blk[source_off + i].file != last_file){	// 別のファイルなら開く
					last_file = s_blk[source_off + i].file;
					if (hFile){
						CloseHandle(hFile);	// 前のファイルを閉じる
						hFile = NULL;
					}
					if ((files[last_file].state & 7) != 0){	// 作り直した作業ファイルから読み込む
						get_temp_name(list_buf + files[last_file].name, file_path + base_len);
					} else if ((files[last_file].state & 32) != 0){	// 名前訂正失敗時には別名ファイルから読み込む
						wcscpy(file_path + base_len, list_buf + files[last_file].name2);
					} else {	// 完全なソース・ファイルから読み込む (追加訂正失敗時も)
						wcscpy(file_path + base_len, list_buf + files[last_file].name);
					}
					hFile = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
					if (hFile == INVALID_HANDLE_VALUE){
						print_win32_err();
						hFile = NULL;
						printf_cp("cannot open file, %s\n", file_path);
						err = 1;
						goto error_end;
					}
				}
				len = s_blk[source_off + i].size;
				file_off = (source_off + i - files[last_file].b_off) * (__int64)block_size;
				if (file_read_data(hFile, file_off, read_buf, len)){
					printf("file_read_data, input slice %d\n", source_off + i);
					err = 1;
					goto error_end;
				}
				if (len < block_size)
					memset(read_buf + len, 0, block_size - len);
				// ソース・ブロックのチェックサムを計算する
				checksum16_altmap(read_buf, read_buf + unit_size - HASH_SIZE, unit_size - HASH_SIZE);
				// chunk ごとに並び替える
				arrange_copy(buf, read_buf, unit_size, i, read_num, chunk_num, chunk_size);
#ifdef TIMER
read_count++;
#endif
			}

			if (i + 1 < read_num){	// 最後のブロック以外なら
				// サブ・スレッドの動作状況を調べる
				j = WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, 0);
				if ((j != WAIT_TIMEOUT) && (j != WAIT_FAILED)){	// 計算中でないなら
					// 経過表示
					prog_num += block_lost;
					if (GetTickCount() - time_last >= UPDATE_TIME){
						if (print_progress((int)((prog_num * 1000) / prog_base))){
							err = 2;
							goto error_end;
						}
						time_last = GetTickCount();
					}
					// 計算終了したブロックの次から計算を開始する
					th->off += 1;
					th->mat = mat + th->off;
					th->now = -1;	// 初期値 - 1
					//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
					for (j = 0; j < (cpu_num + 1) / 2; j++){
						ResetEvent(hEnd[j]);	// リセットしておく
						SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
					}
				}
			}
		}
		if (hFile){	// 最後のソース・ファイルを閉じる
			CloseHandle(hFile);
			hFile = NULL;
		}
#ifdef TIMER
time_read += GetTickCount() - time_start;
#endif

		WaitForMultipleObjects((cpu_num + 1) / 2, hEnd, TRUE, INFINITE);	// サブ・スレッドの計算終了の合図を待つ
		th->off += 1;	// 計算を開始するソース・ブロックの番号
		if (th->off == 0)	// エラーや実験時以外は th->off は 0 にならない
			memset(p_buf, 0, (size_t)unit_size * block_lost);
#ifdef TIMER
		j = (th->off - source_off) * 1000 / read_num;
		printf("partial decode = %d (%d.%d%%), read = %d\n", th->off - source_off, j / 10, j % 10, read_count);
#endif
		recv_now = -1;	// 消失ブロックの本来のソース番号
		last_file = -1;

		// スレッドごとに消失ブロックを計算する
		src_num = read_num - (th->off - source_off);	// 一度に処理する量 (src_num > 0)
		th->mat = mat + th->off;
		// th->off はソース・ブロックの番号
		th->size = src_num;
		th->now = -1;	// 初期値 - 1
		//_mm_sfence();	// メモリーへの書き込みを完了してからスレッドを再開する
		for (j = 0; j < cpu_num; j++){
			ResetEvent(hEnd[j]);	// リセットしておく
			SetEvent(hRun[j]);	// サブ・スレッドに計算を開始させる
		}

		// サブ・スレッドの計算終了の合図を UPDATE_TIME だけ待つ
		while (WaitForMultipleObjects(cpu_num, hEnd, TRUE, UPDATE_TIME) == WAIT_TIMEOUT){
			// th-now が最高値なので、計算が終わってるのは th-now - cpu_num 個となる
			j = th->now - cpu_num;
			if (j < 0)
				j = 0;
			j /= chunk_num;	// chunk数で割ってブロック数にする
			// 経過表示（UPDATE_TIME 時間待った場合なので、必ず経過してるはず）
			if (print_progress((int)(((prog_num + src_num * j) * 1000) / prog_base))){
				err = 2;
				goto error_end;
			}
			time_last = GetTickCount();
		}

		// 経過表示
		prog_num += src_num * block_lost;
		if (GetTickCount() - time_last >= UPDATE_TIME){
			if (print_progress((int)((prog_num * 1000) / prog_base))){
				err = 2;
				goto error_end;
			}
			time_last = GetTickCount();
		}

		source_off += read_num;
	}
	//printf("\nprog_num = %I64d / %I64d\n", prog_num, prog_base);

#ifdef TIMER
time_start = GetTickCount();
#endif
	// 復元されたブロックを書き込む
	work_buf = p_buf;
	for (i = 0; i < block_lost; i++){
		for (j = recv_now + 1; j < source_num; j++){	// 何番のソース・ブロックか
			if (s_blk[j].exist == 0){
				recv_now = j;
				break;
			}
		}
		//printf(" lost block[%d] = source block[%d]\n", i, recv_now);

		// 復元されたソース・ブロックのチェックサムを検証する
		checksum16_return(work_buf, hash, unit_size - HASH_SIZE);
		if (memcmp(work_buf + unit_size - HASH_SIZE, hash, HASH_SIZE) != 0){
			printf("checksum mismatch, recovered input slice %d\n", recv_now);
			err = 1;
			goto error_end;
		}
		// 作業用のソース・ファイルに書き込む
		if (s_blk[recv_now].file != last_file){	// 別のファイルなら開く
			last_file = s_blk[recv_now].file;
			if (hFile){
				CloseHandle(hFile);	// 前のファイルを閉じる
				hFile = NULL;
			}
			hFile = handle_temp_file(list_buf + files[last_file].name, file_path);
			if (hFile == INVALID_HANDLE_VALUE){
				hFile = NULL;
				err = 1;
				goto error_end;
			}
			//printf("file %d, open %S\n", last_file, file_path);
		}
		if (file_write_data(hFile, (recv_now - files[last_file].b_off) * (__int64)block_size, work_buf, s_blk[recv_now].size)){
			printf("file_write_data, input slice %d\n", recv_now);
			err = 1;
			goto error_end;
		}
		work_buf += unit_size;

		// 経過表示
		prog_num += prog_write;
		if (GetTickCount() - time_last >= UPDATE_TIME){
			if (print_progress((int)((prog_num * 1000) / prog_base))){
				err = 2;
				goto error_end;
			}
			time_last = GetTickCount();
		}
	}
#ifdef TIMER
time_write += GetTickCount() - time_start;
#endif
	// 最後の作業ファイルを閉じる
	CloseHandle(hFile);
	hFile = NULL;
	print_progress_done();
	//printf("prog_num = %I64d / %I64d\n", prog_num, prog_base);

#ifdef TIMER
printf("read   %d.%03d sec\n", time_read / 1000, time_read % 1000);
printf("write  %d.%03d sec\n", time_write / 1000, time_write % 1000);
#endif

error_end:
	InterlockedExchange(&(th->now), INT_MAX / 2);	// サブ・スレッドの計算を中断する
	for (j = 0; j < cpu_num; j++){
		if (hSub[j]){	// サブ・スレッドを終了させる
			SetEvent(hRun[j]);
			WaitForSingleObject(hSub[j], INFINITE);
			CloseHandle(hSub[j]);
		}
	}
	if (hFile)
		CloseHandle(hFile);
	if (buf)
		_aligned_free(buf);
	return err;
}

