| // This is free and unencumbered software released into the public domain. | 
 | // | 
 | // Anyone is free to copy, modify, publish, use, compile, sell, or | 
 | // distribute this software, either in source code form or as a compiled | 
 | // binary, for any purpose, commercial or non-commercial, and by any | 
 | // means. | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <unistd.h> | 
 | #include <string.h> | 
 | #include <errno.h> | 
 | #include <vector> | 
 | #include <map> | 
 |  | 
 | int verbose = 0; | 
 |  | 
 | static void push_int_bits(std::vector<bool> &outbits, int value, int bits) | 
 | { | 
 | 	while (bits-- > 0) | 
 | 		outbits.push_back((value >> bits) & 1); | 
 | } | 
 |  | 
 | static void push_zero_bits(std::vector<bool> &outbits, int bits) | 
 | { | 
 | 	while (bits-- > 0) | 
 | 		outbits.push_back(false); | 
 | } | 
 |  | 
 | static int decode_int_from_bits(const std::vector<bool> &inbits, int &cursor, int bits) | 
 | { | 
 | 	int ret = 0; | 
 | 	while (bits-- > 0) | 
 | 		if (inbits.at(cursor++)) | 
 | 			ret |= 1 << bits; | 
 | 	return ret; | 
 | } | 
 |  | 
 | void ice_compress(std::vector<bool> &outbits, const std::vector<bool> &inbits) | 
 | { | 
 | 	int opcode_stats_d4 = 0; | 
 | 	int opcode_stats_d32 = 0; | 
 | 	int opcode_stats_d256 = 0; | 
 | 	int opcode_stats_raw = 0; | 
 | 	int opcode_stats_d8M = 0; | 
 | 	int opcode_stats_end = 0; | 
 |  | 
 | 	std::vector<int> deltas; | 
 | 	int numzeros = 0; | 
 |  | 
 | 	for (auto bit : inbits) | 
 | 	{ | 
 | 		if (bit) { | 
 | 			deltas.push_back(numzeros); | 
 | 			numzeros = 0; | 
 | 		} else { | 
 | 			numzeros++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for (int i = 0; i < int(deltas.size()); i++) | 
 | 	{ | 
 | 		int raw_len = 0; | 
 | 		int compr_len = 0; | 
 | 		int best_compr_raw_diff = -1; | 
 | 		int best_compr_raw_idx = -1; | 
 | 		int best_compr_raw_len = -1; | 
 |  | 
 | 		for (int j = 0; j+i < int(deltas.size()); j++) | 
 | 		{ | 
 | 			int delta = deltas.at(i + j); | 
 | 			raw_len += delta + 1; | 
 |  | 
 | 			if (delta < 4) | 
 | 				compr_len += 3; | 
 | 			else if (delta < 32) | 
 | 				compr_len += 7; | 
 | 			else if (delta < 256) | 
 | 				compr_len += 11; | 
 | 			else | 
 | 				compr_len += 26; | 
 |  | 
 | 			if (compr_len - raw_len < std::max(best_compr_raw_diff - 4, 0) || raw_len > 64) | 
 | 				break; | 
 |  | 
 | 			if (compr_len - raw_len > best_compr_raw_diff) { | 
 | 				best_compr_raw_diff = compr_len - raw_len; | 
 | 				best_compr_raw_idx = j; | 
 | 				best_compr_raw_len = raw_len; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (best_compr_raw_diff > 9) | 
 | 		{ | 
 | 			opcode_stats_raw++; | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(true); | 
 | 			push_int_bits(outbits, best_compr_raw_len-1, 6); | 
 |  | 
 | 			for (int j = 0; j <= best_compr_raw_idx; j++) { | 
 | 				int delta = deltas.at(i + j); | 
 | 				for (int k = 0; k < delta; k++) | 
 | 					outbits.push_back(false); | 
 | 				if (j < best_compr_raw_idx) | 
 | 					outbits.push_back(true); | 
 | 			} | 
 |  | 
 | 			i += best_compr_raw_idx; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		int delta = deltas.at(i); | 
 |  | 
 | 		if (delta < 4) { | 
 | 			opcode_stats_d4++; | 
 | 			outbits.push_back(true); | 
 | 			push_int_bits(outbits, delta, 2); | 
 | 		} else | 
 | 		if (delta < 32) { | 
 | 			opcode_stats_d32++; | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(true); | 
 | 			push_int_bits(outbits, delta, 5); | 
 | 		} else | 
 | 		if (delta < 256) { | 
 | 			opcode_stats_d256++; | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(true); | 
 | 			push_int_bits(outbits, delta, 8); | 
 | 		} else { | 
 | 			opcode_stats_d8M++; | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(false); | 
 | 			outbits.push_back(true); | 
 | 			push_int_bits(outbits, delta, 23); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	opcode_stats_end++; | 
 | 	outbits.push_back(false); | 
 | 	outbits.push_back(false); | 
 | 	outbits.push_back(false); | 
 | 	outbits.push_back(false); | 
 | 	outbits.push_back(false); | 
 | 	push_int_bits(outbits, numzeros, 23); | 
 |  | 
 | 	if (verbose > 1) { | 
 | 		fprintf(stderr, "opcode d4   %5d\n", opcode_stats_d4); | 
 | 		fprintf(stderr, "opcode d32  %5d\n", opcode_stats_d32); | 
 | 		fprintf(stderr, "opcode d256 %5d\n", opcode_stats_d256); | 
 | 		fprintf(stderr, "opcode raw  %5d\n", opcode_stats_raw); | 
 | 		fprintf(stderr, "opcode d8M  %5d\n", opcode_stats_d8M); | 
 | 		fprintf(stderr, "opcode end  %5d\n", opcode_stats_end); | 
 | 	} | 
 | } | 
 |  | 
 | void ice_uncompress(std::vector<bool> &outbits, const std::vector<bool> &inbits) | 
 | { | 
 | 	int cursor = 0; | 
 |  | 
 | 	while (cursor < int(inbits.size())) | 
 | 	{ | 
 | 		if (inbits.at(cursor++)) { | 
 | 			int zeros = decode_int_from_bits(inbits, cursor, 2); | 
 | 			push_zero_bits(outbits, zeros); | 
 | 			outbits.push_back(true); | 
 | 		} else | 
 | 		if (inbits.at(cursor++)) { | 
 | 			int zeros = decode_int_from_bits(inbits, cursor, 5); | 
 | 			push_zero_bits(outbits, zeros); | 
 | 			outbits.push_back(true); | 
 | 		} else | 
 | 		if (inbits.at(cursor++)) { | 
 | 			int zeros = decode_int_from_bits(inbits, cursor, 8); | 
 | 			push_zero_bits(outbits, zeros); | 
 | 			outbits.push_back(true); | 
 | 		} else | 
 | 		if (inbits.at(cursor++)) { | 
 | 			int raw_len = decode_int_from_bits(inbits, cursor, 6); | 
 | 			while (raw_len--) | 
 | 				outbits.push_back(inbits.at(cursor++)); | 
 | 			outbits.push_back(true); | 
 | 		} else | 
 | 		if (inbits.at(cursor++)) { | 
 | 			int zeros = decode_int_from_bits(inbits, cursor, 23); | 
 | 			push_zero_bits(outbits, zeros); | 
 | 			outbits.push_back(true); | 
 | 		} else { | 
 | 			int zeros = decode_int_from_bits(inbits, cursor, 23); | 
 | 			push_zero_bits(outbits, zeros); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void help() | 
 | { | 
 | 	printf("\n"); | 
 | 	printf("Usage: icecompr [-v] [input-file [output-file]]\n"); | 
 | 	printf("\n"); | 
 | 	exit(1); | 
 | } | 
 |  | 
 | int main(int argc, char **argv) | 
 | { | 
 | 	FILE *input_file = stdin; | 
 | 	FILE *output_file = stdout; | 
 |  | 
 | 	int opt; | 
 | 	while ((opt = getopt(argc, argv, "v")) != -1) | 
 | 	{ | 
 | 		switch (opt) | 
 | 		{ | 
 | 		case 'v': | 
 | 			verbose++; | 
 | 			break; | 
 | 		default: | 
 | 			help(); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (optind < argc) { | 
 | 		input_file = fopen(argv[optind], "rb"); | 
 | 		if (input_file == NULL) { | 
 | 			fprintf(stderr, "Failed to open input file `%s': %s\n", argv[optind], strerror(errno)); | 
 | 			return 1; | 
 | 		} | 
 | 		optind++; | 
 | 	} | 
 |  | 
 | 	if (optind < argc) { | 
 | 		output_file = fopen(argv[optind], "wb"); | 
 | 		if (output_file == NULL) { | 
 | 			fprintf(stderr, "Failed to open output file `%s': %s\n", argv[optind], strerror(errno)); | 
 | 			return 1; | 
 | 		} | 
 | 		optind++; | 
 | 	} | 
 |  | 
 | 	if (optind != argc) | 
 | 		help(); | 
 |  | 
 | 	std::vector<bool> original_bits; | 
 | 	int count_set_bits = 0; | 
 |  | 
 | 	while (1) | 
 | 	{ | 
 | 		int byte = fgetc(input_file); | 
 |  | 
 | 		if (byte < 0) | 
 | 			break; | 
 |  | 
 | 		// MSB first | 
 | 		for (int i = 7; i >= 0; i--) { | 
 | 			bool bit = (byte >> i) & 1; | 
 | 			if (bit) count_set_bits++; | 
 | 			original_bits.push_back(bit); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	int uncompressed_size = original_bits.size(); | 
 |  | 
 | 	if (verbose > 0) { | 
 | 		fprintf(stderr, "Percentage of set bits: %.2f%%\n", (100.0*count_set_bits) / uncompressed_size); | 
 | 		fprintf(stderr, "Uncompressed size: %8d bits\n", uncompressed_size); | 
 | 	} | 
 |  | 
 | 	std::vector<bool> compressed_bits; | 
 | 	ice_compress(compressed_bits, original_bits); | 
 |  | 
 | 	int compressed_size = compressed_bits.size(); | 
 |  | 
 | 	if (verbose > 0) { | 
 | 		fprintf(stderr, "Compressed size:   %8d bits\n", compressed_size); | 
 | 		fprintf(stderr, "Space savings: %.2f%%\n", 100 - (100.0*compressed_size) / uncompressed_size); | 
 | 	} | 
 |  | 
 | 	std::vector<bool> uncompressed_bits; | 
 | 	ice_uncompress(uncompressed_bits, compressed_bits); | 
 |  | 
 | 	bool check_ok = original_bits == uncompressed_bits; | 
 |  | 
 | 	if (verbose > 0 || !check_ok) { | 
 | 		fprintf(stderr, "Integrity check: %s\n", check_ok ? "OK" : "ERROR"); | 
 | 		if (!check_ok) | 
 | 			return 1; | 
 | 	} | 
 |  | 
 | 	fprintf(output_file, "ICECOMPR"); | 
 | 	for (int i = 0; i < int(compressed_bits.size()); i += 8) { | 
 | 		int value = 0; | 
 | 		for (int j = 0; j < 8 && i+j < int(compressed_bits.size()); j++) | 
 | 			if (compressed_bits.at(i+j)) | 
 | 				value |= 1 << (7-j); | 
 | 		fputc(value, output_file); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  |