File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / match.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 07:51:14 2013 UTC (10 years, 8 months ago) by misho
Branches: rsync, MAIN
CVS tags: RSYNC3_1_0, HEAD
v 3.1.0

    1: /*
    2:  * Block matching used by the file-transfer code.
    3:  *
    4:  * Copyright (C) 1996 Andrew Tridgell
    5:  * Copyright (C) 1996 Paul Mackerras
    6:  * Copyright (C) 2003-2013 Wayne Davison
    7:  *
    8:  * This program is free software; you can redistribute it and/or modify
    9:  * it under the terms of the GNU General Public License as published by
   10:  * the Free Software Foundation; either version 3 of the License, or
   11:  * (at your option) any later version.
   12:  *
   13:  * This program is distributed in the hope that it will be useful,
   14:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16:  * GNU General Public License for more details.
   17:  *
   18:  * You should have received a copy of the GNU General Public License along
   19:  * with this program; if not, visit the http://fsf.org website.
   20:  */
   21: 
   22: #include "rsync.h"
   23: #include "inums.h"
   24: 
   25: extern int checksum_seed;
   26: extern int append_mode;
   27: extern int checksum_len;
   28: 
   29: int updating_basis_file;
   30: char sender_file_sum[MAX_DIGEST_LEN];
   31: 
   32: static int false_alarms;
   33: static int hash_hits;
   34: static int matches;
   35: static int64 data_transfer;
   36: 
   37: static int total_false_alarms;
   38: static int total_hash_hits;
   39: static int total_matches;
   40: 
   41: extern struct stats stats;
   42: 
   43: #define TRADITIONAL_TABLESIZE (1<<16)
   44: 
   45: static uint32 tablesize;
   46: static int32 *hash_table;
   47: 
   48: #define SUM2HASH2(s1,s2) (((s1) + (s2)) & 0xFFFF)
   49: #define SUM2HASH(sum) SUM2HASH2((sum)&0xFFFF,(sum)>>16)
   50: 
   51: #define BIG_SUM2HASH(sum) ((sum)%tablesize)
   52: 
   53: static void build_hash_table(struct sum_struct *s)
   54: {
   55: 	static uint32 alloc_size;
   56: 	int32 i;
   57: 
   58: 	/* Dynamically calculate the hash table size so that the hash load
   59: 	 * for big files is about 80%.  A number greater than the traditional
   60: 	 * size must be odd or s2 will not be able to span the entire set. */
   61: 	tablesize = (uint32)(s->count/8) * 10 + 11;
   62: 	if (tablesize < TRADITIONAL_TABLESIZE)
   63: 		tablesize = TRADITIONAL_TABLESIZE;
   64: 	if (tablesize > alloc_size || tablesize < alloc_size - 16*1024) {
   65: 		if (hash_table)
   66: 			free(hash_table);
   67: 		hash_table = new_array(int32, tablesize);
   68: 		if (!hash_table)
   69: 			out_of_memory("build_hash_table");
   70: 		alloc_size = tablesize;
   71: 	}
   72: 
   73: 	memset(hash_table, 0xFF, tablesize * sizeof hash_table[0]);
   74: 
   75: 	if (tablesize == TRADITIONAL_TABLESIZE) {
   76: 		for (i = 0; i < s->count; i++) {
   77: 			uint32 t = SUM2HASH(s->sums[i].sum1);
   78: 			s->sums[i].chain = hash_table[t];
   79: 			hash_table[t] = i;
   80: 		}
   81: 	} else {
   82: 		for (i = 0; i < s->count; i++) {
   83: 			uint32 t = BIG_SUM2HASH(s->sums[i].sum1);
   84: 			s->sums[i].chain = hash_table[t];
   85: 			hash_table[t] = i;
   86: 		}
   87: 	}
   88: }
   89: 
   90: 
   91: static OFF_T last_match;
   92: 
   93: 
   94: /* Transmit a literal and/or match token.
   95:  *
   96:  * This delightfully-named function is called either when we find a
   97:  * match and need to transmit all the unmatched data leading up to it,
   98:  * or when we get bored of accumulating literal data and just need to
   99:  * transmit it.  As a result of this second case, it is called even if
  100:  * we have not matched at all!
  101:  *
  102:  * If i >= 0, the number of a matched token.  If < 0, indicates we have
  103:  * only literal data.  A -1 will send a 0-token-int too, and a -2 sends
  104:  * only literal data, w/o any token-int. */
  105: static void matched(int f, struct sum_struct *s, struct map_struct *buf,
  106: 		    OFF_T offset, int32 i)
  107: {
  108: 	int32 n = (int32)(offset - last_match); /* max value: block_size (int32) */
  109: 	int32 j;
  110: 
  111: 	if (DEBUG_GTE(DELTASUM, 2) && i >= 0) {
  112: 		rprintf(FINFO,
  113: 			"match at %s last_match=%s j=%d len=%ld n=%ld\n",
  114: 			big_num(offset), big_num(last_match), i,
  115: 			(long)s->sums[i].len, (long)n);
  116: 	}
  117: 
  118: 	send_token(f, i, buf, last_match, n, i < 0 ? 0 : s->sums[i].len);
  119: 	data_transfer += n;
  120: 
  121: 	if (i >= 0) {
  122: 		stats.matched_data += s->sums[i].len;
  123: 		n += s->sums[i].len;
  124: 	}
  125: 
  126: 	for (j = 0; j < n; j += CHUNK_SIZE) {
  127: 		int32 n1 = MIN(CHUNK_SIZE, n - j);
  128: 		sum_update(map_ptr(buf, last_match + j, n1), n1);
  129: 	}
  130: 
  131: 	if (i >= 0)
  132: 		last_match = offset + s->sums[i].len;
  133: 	else
  134: 		last_match = offset;
  135: 
  136: 	if (buf && INFO_GTE(PROGRESS, 1))
  137: 		show_progress(last_match, buf->file_size);
  138: }
  139: 
  140: 
  141: static void hash_search(int f,struct sum_struct *s,
  142: 			struct map_struct *buf, OFF_T len)
  143: {
  144: 	OFF_T offset, aligned_offset, end;
  145: 	int32 k, want_i, aligned_i, backup;
  146: 	char sum2[SUM_LENGTH];
  147: 	uint32 s1, s2, sum;
  148: 	int more;
  149: 	schar *map;
  150: 
  151: 	/* want_i is used to encourage adjacent matches, allowing the RLL
  152: 	 * coding of the output to work more efficiently. */
  153: 	want_i = 0;
  154: 
  155: 	if (DEBUG_GTE(DELTASUM, 2)) {
  156: 		rprintf(FINFO, "hash search b=%ld len=%s\n",
  157: 			(long)s->blength, big_num(len));
  158: 	}
  159: 
  160: 	k = (int32)MIN(len, (OFF_T)s->blength);
  161: 
  162: 	map = (schar *)map_ptr(buf, 0, k);
  163: 
  164: 	sum = get_checksum1((char *)map, k);
  165: 	s1 = sum & 0xFFFF;
  166: 	s2 = sum >> 16;
  167: 	if (DEBUG_GTE(DELTASUM, 3))
  168: 		rprintf(FINFO, "sum=%.8x k=%ld\n", sum, (long)k);
  169: 
  170: 	offset = aligned_offset = aligned_i = 0;
  171: 
  172: 	end = len + 1 - s->sums[s->count-1].len;
  173: 
  174: 	if (DEBUG_GTE(DELTASUM, 3)) {
  175: 		rprintf(FINFO, "hash search s->blength=%ld len=%s count=%s\n",
  176: 			(long)s->blength, big_num(len), big_num(s->count));
  177: 	}
  178: 
  179: 	do {
  180: 		int done_csum2 = 0;
  181: 		uint32 hash_entry;
  182: 		int32 i, *prev;
  183: 
  184: 		if (DEBUG_GTE(DELTASUM, 4)) {
  185: 			rprintf(FINFO, "offset=%s sum=%04x%04x\n",
  186: 				big_num(offset), s2 & 0xFFFF, s1 & 0xFFFF);
  187: 		}
  188: 
  189: 		if (tablesize == TRADITIONAL_TABLESIZE) {
  190: 			hash_entry = SUM2HASH2(s1,s2);
  191: 			if ((i = hash_table[hash_entry]) < 0)
  192: 				goto null_hash;
  193: 			sum = (s1 & 0xffff) | (s2 << 16);
  194: 		} else {
  195: 			sum = (s1 & 0xffff) | (s2 << 16);
  196: 			hash_entry = BIG_SUM2HASH(sum);
  197: 			if ((i = hash_table[hash_entry]) < 0)
  198: 				goto null_hash;
  199: 		}
  200: 		prev = &hash_table[hash_entry];
  201: 
  202: 		hash_hits++;
  203: 		do {
  204: 			int32 l;
  205: 
  206: 			/* When updating in-place, the chunk's offset must be
  207: 			 * either >= our offset or identical data at that offset.
  208: 			 * Remove any bypassed entries that we can never use. */
  209: 			if (updating_basis_file && s->sums[i].offset < offset
  210: 			    && !(s->sums[i].flags & SUMFLG_SAME_OFFSET)) {
  211: 				*prev = s->sums[i].chain;
  212: 				continue;
  213: 			}
  214: 			prev = &s->sums[i].chain;
  215: 
  216: 			if (sum != s->sums[i].sum1)
  217: 				continue;
  218: 
  219: 			/* also make sure the two blocks are the same length */
  220: 			l = (int32)MIN((OFF_T)s->blength, len-offset);
  221: 			if (l != s->sums[i].len)
  222: 				continue;
  223: 
  224: 			if (DEBUG_GTE(DELTASUM, 3)) {
  225: 				rprintf(FINFO,
  226: 					"potential match at %s i=%ld sum=%08x\n",
  227: 					big_num(offset), (long)i, sum);
  228: 			}
  229: 
  230: 			if (!done_csum2) {
  231: 				map = (schar *)map_ptr(buf,offset,l);
  232: 				get_checksum2((char *)map,l,sum2);
  233: 				done_csum2 = 1;
  234: 			}
  235: 
  236: 			if (memcmp(sum2,s->sums[i].sum2,s->s2length) != 0) {
  237: 				false_alarms++;
  238: 				continue;
  239: 			}
  240: 
  241: 			/* When updating in-place, the best possible match is
  242: 			 * one with an identical offset, so we prefer that over
  243: 			 * the adjacent want_i optimization. */
  244: 			if (updating_basis_file) {
  245: 				/* All the generator's chunks start at blength boundaries. */
  246: 				while (aligned_offset < offset) {
  247: 					aligned_offset += s->blength;
  248: 					aligned_i++;
  249: 				}
  250: 				if ((offset == aligned_offset
  251: 				  || (sum == 0 && l == s->blength && aligned_offset + l <= len))
  252: 				 && aligned_i < s->count) {
  253: 					if (i != aligned_i) {
  254: 						if (sum != s->sums[aligned_i].sum1
  255: 						 || l != s->sums[aligned_i].len
  256: 						 || memcmp(sum2, s->sums[aligned_i].sum2, s->s2length) != 0)
  257: 							goto check_want_i;
  258: 						i = aligned_i;
  259: 					}
  260: 					if (offset != aligned_offset) {
  261: 						/* We've matched some zeros in a spot that is also zeros
  262: 						 * further along in the basis file, if we find zeros ahead
  263: 						 * in the sender's file, we'll output enough literal data
  264: 						 * to re-align with the basis file, and get back to seeking
  265: 						 * instead of writing. */
  266: 						backup = (int32)(aligned_offset - last_match);
  267: 						if (backup < 0)
  268: 							backup = 0;
  269: 						map = (schar *)map_ptr(buf, aligned_offset - backup, l + backup)
  270: 						    + backup;
  271: 						sum = get_checksum1((char *)map, l);
  272: 						if (sum != s->sums[i].sum1)
  273: 							goto check_want_i;
  274: 						get_checksum2((char *)map, l, sum2);
  275: 						if (memcmp(sum2, s->sums[i].sum2, s->s2length) != 0)
  276: 							goto check_want_i;
  277: 						/* OK, we have a re-alignment match.  Bump the offset
  278: 						 * forward to the new match point. */
  279: 						offset = aligned_offset;
  280: 					}
  281: 					/* This identical chunk is in the same spot in the old and new file. */
  282: 					s->sums[i].flags |= SUMFLG_SAME_OFFSET;
  283: 					want_i = i;
  284: 				}
  285: 			}
  286: 
  287: 		  check_want_i:
  288: 			/* we've found a match, but now check to see
  289: 			 * if want_i can hint at a better match. */
  290: 			if (i != want_i && want_i < s->count
  291: 			    && (!updating_basis_file || s->sums[want_i].offset >= offset
  292: 			     || s->sums[want_i].flags & SUMFLG_SAME_OFFSET)
  293: 			    && sum == s->sums[want_i].sum1
  294: 			    && memcmp(sum2, s->sums[want_i].sum2, s->s2length) == 0) {
  295: 				/* we've found an adjacent match - the RLL coder
  296: 				 * will be happy */
  297: 				i = want_i;
  298: 			}
  299: 			want_i = i + 1;
  300: 
  301: 			matched(f,s,buf,offset,i);
  302: 			offset += s->sums[i].len - 1;
  303: 			k = (int32)MIN((OFF_T)s->blength, len-offset);
  304: 			map = (schar *)map_ptr(buf, offset, k);
  305: 			sum = get_checksum1((char *)map, k);
  306: 			s1 = sum & 0xFFFF;
  307: 			s2 = sum >> 16;
  308: 			matches++;
  309: 			break;
  310: 		} while ((i = s->sums[i].chain) >= 0);
  311: 
  312: 	  null_hash:
  313: 		backup = (int32)(offset - last_match);
  314: 		/* We sometimes read 1 byte prior to last_match... */
  315: 		if (backup < 0)
  316: 			backup = 0;
  317: 
  318: 		/* Trim off the first byte from the checksum */
  319: 		more = offset + k < len;
  320: 		map = (schar *)map_ptr(buf, offset - backup, k + more + backup)
  321: 		    + backup;
  322: 		s1 -= map[0] + CHAR_OFFSET;
  323: 		s2 -= k * (map[0]+CHAR_OFFSET);
  324: 
  325: 		/* Add on the next byte (if there is one) to the checksum */
  326: 		if (more) {
  327: 			s1 += map[k] + CHAR_OFFSET;
  328: 			s2 += s1;
  329: 		} else
  330: 			--k;
  331: 
  332: 		/* By matching early we avoid re-reading the
  333: 		   data 3 times in the case where a token
  334: 		   match comes a long way after last
  335: 		   match. The 3 reads are caused by the
  336: 		   running match, the checksum update and the
  337: 		   literal send. */
  338: 		if (backup >= s->blength+CHUNK_SIZE && end-offset > CHUNK_SIZE)
  339: 			matched(f, s, buf, offset - s->blength, -2);
  340: 	} while (++offset < end);
  341: 
  342: 	matched(f, s, buf, len, -1);
  343: 	map_ptr(buf, len-1, 1);
  344: }
  345: 
  346: 
  347: /**
  348:  * Scan through a origin file, looking for sections that match
  349:  * checksums from the generator, and transmit either literal or token
  350:  * data.
  351:  *
  352:  * Also calculates the MD4 checksum of the whole file, using the md
  353:  * accumulator.  This is transmitted with the file as protection
  354:  * against corruption on the wire.
  355:  *
  356:  * @param s Checksums received from the generator.  If <tt>s->count ==
  357:  * 0</tt>, then there are actually no checksums for this file.
  358:  *
  359:  * @param len Length of the file to send.
  360:  **/
  361: void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len)
  362: {
  363: 	last_match = 0;
  364: 	false_alarms = 0;
  365: 	hash_hits = 0;
  366: 	matches = 0;
  367: 	data_transfer = 0;
  368: 
  369: 	sum_init(checksum_seed);
  370: 
  371: 	if (append_mode > 0) {
  372: 		if (append_mode == 2) {
  373: 			OFF_T j = 0;
  374: 			for (j = CHUNK_SIZE; j < s->flength; j += CHUNK_SIZE) {
  375: 				if (buf && INFO_GTE(PROGRESS, 1))
  376: 					show_progress(last_match, buf->file_size);
  377: 				sum_update(map_ptr(buf, last_match, CHUNK_SIZE),
  378: 					   CHUNK_SIZE);
  379: 				last_match = j;
  380: 			}
  381: 			if (last_match < s->flength) {
  382: 				int32 n = (int32)(s->flength - last_match);
  383: 				if (buf && INFO_GTE(PROGRESS, 1))
  384: 					show_progress(last_match, buf->file_size);
  385: 				sum_update(map_ptr(buf, last_match, n), n);
  386: 			}
  387: 		}
  388: 		last_match = s->flength;
  389: 		s->count = 0;
  390: 	}
  391: 
  392: 	if (len > 0 && s->count > 0) {
  393: 		build_hash_table(s);
  394: 
  395: 		if (DEBUG_GTE(DELTASUM, 2))
  396: 			rprintf(FINFO,"built hash table\n");
  397: 
  398: 		hash_search(f, s, buf, len);
  399: 
  400: 		if (DEBUG_GTE(DELTASUM, 2))
  401: 			rprintf(FINFO,"done hash search\n");
  402: 	} else {
  403: 		OFF_T j;
  404: 		/* by doing this in pieces we avoid too many seeks */
  405: 		for (j = last_match + CHUNK_SIZE; j < len; j += CHUNK_SIZE)
  406: 			matched(f, s, buf, j, -2);
  407: 		matched(f, s, buf, len, -1);
  408: 	}
  409: 
  410: 	if (sum_end(sender_file_sum) != checksum_len)
  411: 		overflow_exit("checksum_len"); /* Impossible... */
  412: 
  413: 	/* If we had a read error, send a bad checksum.  We use all bits
  414: 	 * off as long as the checksum doesn't happen to be that, in
  415: 	 * which case we turn the last 0 bit into a 1. */
  416: 	if (buf && buf->status != 0) {
  417: 		int i;
  418: 		for (i = 0; i < checksum_len && sender_file_sum[i] == 0; i++) {}
  419: 		memset(sender_file_sum, 0, checksum_len);
  420: 		if (i == checksum_len)
  421: 			sender_file_sum[i-1]++;
  422: 	}
  423: 
  424: 	if (DEBUG_GTE(DELTASUM, 2))
  425: 		rprintf(FINFO,"sending file_sum\n");
  426: 	write_buf(f, sender_file_sum, checksum_len);
  427: 
  428: 	if (DEBUG_GTE(DELTASUM, 2)) {
  429: 		rprintf(FINFO, "false_alarms=%d hash_hits=%d matches=%d\n",
  430: 			false_alarms, hash_hits, matches);
  431: 	}
  432: 
  433: 	total_hash_hits += hash_hits;
  434: 	total_false_alarms += false_alarms;
  435: 	total_matches += matches;
  436: 	stats.literal_data += data_transfer;
  437: }
  438: 
  439: void match_report(void)
  440: {
  441: 	if (!DEBUG_GTE(DELTASUM, 1))
  442: 		return;
  443: 
  444: 	rprintf(FINFO,
  445: 		"total: matches=%d  hash_hits=%d  false_alarms=%d data=%s\n",
  446: 		total_matches, total_hash_hits, total_false_alarms,
  447: 		big_num(stats.literal_data));
  448: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>