From c9c401d15ca1943c5c598abc74cc4f3dd6f77af6 Mon Sep 17 00:00:00 2001
From: arvidn <arvid@cs.umu.se>
Date: Sat, 12 Mar 2016 13:01:34 -0500
Subject: [PATCH] add piece matrix to client_test

---
 examples/client_test.cpp | 48 ++++++++++++++++----
 examples/print.cpp       | 95 ++++++++++++++++++++++++++++++++++++++++
 examples/print.hpp       |  2 +
 3 files changed, 136 insertions(+), 9 deletions(-)

diff --git a/examples/client_test.cpp b/examples/client_test.cpp
index 90a403b10..62e76ab7c 100644
--- a/examples/client_test.cpp
+++ b/examples/client_test.cpp
@@ -173,6 +173,7 @@ bool print_trackers = false;
 bool print_peers = false;
 bool print_log = false;
 bool print_downloads = false;
+bool print_matrix = false;
 bool print_file_progress = false;
 bool show_pad_files = false;
 bool show_dht_status = false;
@@ -377,9 +378,10 @@ FILE* g_log_file = 0;
 std::string const& piece_bar(libtorrent::bitfield const& p, int width)
 {
 #ifdef _WIN32
-	int const table_size = 2;
+	int const table_size = 5;
 #else
 	int const table_size = 18;
+	width *= 2; // we only print one character for every two "slots"
 #endif
 
 	double const piece_per_char = p.size() / double(width);
@@ -396,6 +398,10 @@ std::string const& piece_bar(libtorrent::bitfield const& p, int width)
 
 	// the [piece, piece + pieces_per_char) range is the pieces that are represented by each character
 	double piece = 0;
+
+	// we print two blocks at a time, so calculate the color in pair
+	int color[2];
+	int last_color[2] = { -1, -1};
 	for (int i = 0; i < width; ++i, piece += piece_per_char)
 	{
 		int num_pieces = 0;
@@ -403,15 +409,31 @@ std::string const& piece_bar(libtorrent::bitfield const& p, int width)
 		int end = (std::max)(int(piece + piece_per_char), int(piece) + 1);
 		for (int k = int(piece); k < end; ++k, ++num_pieces)
 			if (p[k]) ++num_have;
-		int color = int(std::ceil(num_have / float((std::max)(num_pieces, 1)) * (table_size - 1)));
+		int const c = int(std::ceil(num_have / float((std::max)(num_pieces, 1)) * (table_size - 1)));
 		char buf[40];
-#ifdef _WIN32
-		snprintf(buf, sizeof(buf), "\x1b[4%dm", color ? 7 : 0);
+		color[i & 1] = c;
+
+#ifndef _WIN32
+		if ((i & 1) == 1)
+		{
+			// now, print color[0] and [1]
+			// bg determines whether we're settings foreground or background color
+			static int const bg[] = { 38, 48};
+			for (int i = 0; i < 2; ++i)
+			{
+				if (color[i] != last_color[i])
+				{
+					snprintf(buf, sizeof(buf), "\x1b[%d;5;%dm", bg[i & 1], 232 + color[i]);
+					last_color[i] = color[i];
+					bar += buf;
+				}
+			}
+			bar += "\u258C";
+		}
 #else
-		snprintf(buf, sizeof(buf), "\x1b[48;5;%dm", 232 + color);
+		static char const table[] = {' ', '\xb0', '\xb1', '\xb2', '\xdb'};
+		bar += table[c];
 #endif
-		bar += buf;
-		bar += " ";
 	}
 	bar += esc("0");
 	bar += "]";
@@ -1913,6 +1935,7 @@ int main(int argc, char* argv[])
 				if (c == 'i') print_peers = !print_peers;
 				if (c == 'l') print_log = !print_log;
 				if (c == 'd') print_downloads = !print_downloads;
+				if (c == 'y') print_matrix = !print_matrix;
 				if (c == 'f') print_file_progress = !print_file_progress;
 				if (c == 'P') show_pad_files = !show_pad_files;
 				if (c == 'g') show_dht_status = !show_dht_status;
@@ -1956,8 +1979,8 @@ int main(int argc, char* argv[])
 						"[i] toggle show peers                           [d] toggle show downloading pieces\n"
 						"[u] show uTP stats                              [f] toggle show files\n"
 						"[g] show DHT                                    [x] toggle disk cache stats\n"
-						"[t] show trackers                               [l] show alert log\n"
-						"[P] show pad files (in file list)\n"
+						"[t] show trackers                               [l] toggle show log\n"
+						"[P] show pad files (in file list)               [y] toggle show piece matrix\n"
 						"\n"
 						"COLUMN OPTIONS\n"
 						"[1] toggle IP column                            [2]\n"
@@ -2096,6 +2119,13 @@ int main(int argc, char* argv[])
 				}
 			}
 
+			if (print_matrix)
+			{
+				int height = 0;
+				print(piece_matrix(s.pieces, terminal_width, &height).c_str());
+				pos += height;
+			}
+
 			if (print_downloads)
 			{
 				h.get_download_queue(queue);
diff --git a/examples/print.cpp b/examples/print.cpp
index 83f888c77..c02ab00bf 100644
--- a/examples/print.cpp
+++ b/examples/print.cpp
@@ -128,6 +128,101 @@ std::string const& progress_bar(int progress, int width, color_code c
 	return bar;
 }
 
+bool get_piece(libtorrent::bitfield const& p, int index)
+{
+	if (index < 0 || index >= p.size()) return false;
+	return p.get_bit(index);
+}
+
+#ifndef _WIN32
+// this function uses the block characters that splits up the glyph in 4
+// segments and provide all combinations of a segment lit or not. This allows us
+// to print 4 pieces per character.
+std::string piece_matrix(libtorrent::bitfield const& p, int width, int* height)
+{
+	// print two rows of pieces at a time
+	int piece = 0;
+	++*height;
+	std::string ret;
+	ret.reserve((p.size() + width * 2 - 1) / width / 2 * 4);
+	while (piece < p.size())
+	{
+		for (int i = 0; i < width; ++i)
+		{
+			// each character has 4 pieces. store them in a byte to use for lookups
+			int const c = get_piece(p, piece)
+				| (get_piece(p, piece+1) << 1)
+				| (get_piece(p, width*2+piece) << 2)
+				| (get_piece(p, width*2+piece+1) << 3);
+
+			// we have 4 bits, 16 different combinations
+			static char const* const chars[] =
+			{
+				" ",      // no bit is set             0000
+				"\u2598", // upper left                0001
+				"\u259d", // upper right               0010
+				"\u2580", // both top bits             0011
+				"\u2596", // lower left                0100
+				"\u258c", // both left bits            0101
+				"\u259e", // upper right, lower left   0110
+				"\u259b", // left and upper sides      0111
+				"\u2597", // lower right               1000
+				"\u259a", // lower right, upper left   1001
+				"\u2590", // right side                1010
+				"\u259c", // lower right, top side     1011
+				"\u2584", // both lower bits           1100
+				"\u2599", // both lower, top left      1101
+				"\u259f", // both lower, top right     1110
+				"\x1b[7m \x1b[27m" // all bits are set (full block)
+			};
+
+			ret += chars[c];
+			piece += 2;
+		}
+		ret += '\n';
+		++*height;
+		piece += width * 2; // skip another row, as we've already printed it
+	}
+	return ret;
+}
+#else
+// on MS-DOS terminals, we only have block characters for upper half and lower
+// half. This lets us print two pieces per character.
+std::string piece_matrix(libtorrent::bitfield const& p, int width, int* height)
+{
+	// print two rows of pieces at a time
+	int piece = 0;
+	++*height;
+	std::string ret;
+	ret.reserve((p.size() + width * 2 - 1) / width);
+	while (piece < p.size())
+	{
+		for (int i = 0; i < width; ++i)
+		{
+			// each character has 8 pieces. store them in a byte to use for lookups
+			// the ordering of these bits
+			int const c = get_piece(p, piece)
+				| (get_piece(p, width*2+piece) << 1);
+
+			static char const* const chars[] =
+			{
+				" ",    // no piece     00
+				"\xdf", // top piece    01
+				"\xdc", // bottom piece 10
+				"\xdb"  // both pieces  11
+			};
+
+			ret += chars[c];
+			++piece;
+		}
+		ret += '\n';
+		++*height;
+		piece += width * 2; // skip another row, as we've already printed it
+	}
+	return ret;
+}
+#endif
+
 void set_cursor_pos(int x, int y)
 {
 #ifdef _WIN32
diff --git a/examples/print.hpp b/examples/print.hpp
index 7ac9e2045..497073153 100644
--- a/examples/print.hpp
+++ b/examples/print.hpp
@@ -2,6 +2,7 @@
 #define PRINT_HPP_
 
 #include <string>
+#include "libtorrent/bitfield.hpp"
 
 enum color_code
 {
@@ -36,6 +37,7 @@ void clear_screen();
 void clear_rows(int y1, int y2);
 
 void terminal_size(int* terminal_width, int* terminal_height);
+std::string piece_matrix(libtorrent::bitfield const& p, int width, int* height);
 
 void print(char const* str);