This attempts to accomplish some per-file memory-savings by moving the
uid+gid items out of the file-list (since their values are common to
multiple file-list entries) and replacing them with an index to an
array of structures.

This only saves 4 bytes per file (not counting the overhead of the array).

This probably needs a hashing algorithm to be added if the uid+gid list
gets to be really large.

--- old/flist.c
+++ new/flist.c
@@ -54,6 +54,7 @@ extern int copy_unsafe_links;
 extern int protocol_version;
 extern int sanitize_paths;
 extern struct stats stats;
+extern struct id_pair *id_pairs;
 extern struct file_list *the_file_list;
 
 extern char curr_dir[MAXPATHLEN];
@@ -347,14 +348,14 @@ static void send_file_entry(struct file_
 		}
 	} else if (protocol_version < 28)
 		rdev = makedev(0, 0);
-	if (file->uid == uid)
+	if (id_pairs[file->id_ndx].uid == uid)
 		flags |= XMIT_SAME_UID;
 	else
-		uid = file->uid;
-	if (file->gid == gid)
+		uid = id_pairs[file->id_ndx].uid;
+	if (id_pairs[file->id_ndx].gid == gid)
 		flags |= XMIT_SAME_GID;
 	else
-		gid = file->gid;
+		gid = id_pairs[file->id_ndx].gid;
 	if (file->modtime == modtime)
 		flags |= XMIT_SAME_TIME;
 	else
@@ -605,8 +606,7 @@ static struct file_struct *receive_file_
 	file->modtime = modtime;
 	file->length = file_length;
 	file->mode = mode;
-	file->uid = uid;
-	file->gid = gid;
+	file->id_ndx = id_pair(uid, gid);
 
 	if (dirname_len) {
 		file->dirname = lastdir = bp;
@@ -855,8 +855,7 @@ struct file_struct *make_file(char *fnam
 	file->modtime = st.st_mtime;
 	file->length = st.st_size;
 	file->mode = st.st_mode;
-	file->uid = st.st_uid;
-	file->gid = st.st_gid;
+	file->id_ndx = id_pair(st.st_uid, st.st_gid);
 
 #ifdef SUPPORT_HARD_LINKS
 	if (flist && flist->hlink_pool) {
@@ -924,8 +923,7 @@ struct file_struct *make_file(char *fnam
 			file->modtime = st2.st_mtime;
 			file->length = st2.st_size;
 			file->mode = st2.st_mode;
-			file->uid = st2.st_uid;
-			file->gid = st2.st_gid;
+			file->id_ndx = id_pair(st2.st_uid, st2.st_gid);
 			file->u.link = NULL;
 		} else
 			file->mode = save_mode;
@@ -1375,7 +1373,7 @@ struct file_list *recv_file_list(int f)
 	clean_flist(flist, relative_paths, 1);
 
 	if (f >= 0) {
-		recv_uid_list(f, flist);
+		recv_uid_list(f);
 
 		/* Recv the io_error flag */
 		if (lp_ignore_errors(module_id) || ignore_errors)
@@ -1691,13 +1689,15 @@ static void output_flist(struct file_lis
 
 	for (i = 0; i < flist->count; i++) {
 		file = flist->files[i];
-		if ((am_root || am_sender) && preserve_uid)
-			sprintf(uidbuf, " uid=%ld", (long)file->uid);
-		else
+		if ((am_root || am_sender) && preserve_uid) {
+			sprintf(uidbuf, " uid=%ld",
+				(long)id_pairs[file->id_ndx].uid);
+		} else
 			*uidbuf = '\0';
-		if (preserve_gid && file->gid != GID_NONE)
-			sprintf(gidbuf, " gid=%ld", (long)file->gid);
-		else
+		if (preserve_gid && id_pairs[file->id_ndx].gid != GID_NONE) {
+			sprintf(gidbuf, " gid=%ld",
+				(long)id_pairs[file->id_ndx].gid);
+		} else
 			*gidbuf = '\0';
 		if (!am_sender)
 			sprintf(depthbuf, "%d", file->dir.depth);
--- old/generator.c
+++ new/generator.c
@@ -90,6 +90,7 @@ extern dev_t filesystem_dev;
 extern char *backup_dir;
 extern char *backup_suffix;
 extern int backup_suffix_len;
+extern struct id_pair *id_pairs;
 extern struct file_list *the_file_list;
 extern struct filter_list_struct server_filter_list;
 
@@ -323,10 +324,12 @@ int unchanged_attrs(struct file_struct *
 	 && (st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS))
 		return 0;
 
-	if (am_root && preserve_uid && st->st_uid != file->uid)
+	if (am_root && preserve_uid
+	 && st->st_uid != id_pairs[file->id_ndx].uid)
 		return 0;
 
-	if (preserve_gid && file->gid != GID_NONE && st->st_gid != file->gid)
+	if (preserve_gid && id_pairs[file->id_ndx].gid != GID_NONE
+	 && st->st_gid != id_pairs[file->id_ndx].gid)
 		return 0;
 
 	return 1;
@@ -339,6 +342,8 @@ void itemize(struct file_struct *file, i
 		int keep_time = !preserve_times ? 0
 		    : S_ISDIR(file->mode) ? !omit_dir_times
 		    : !S_ISLNK(file->mode);
+		uid_t uid = id_pairs[file->id_ndx].uid;
+		gid_t gid = id_pairs[file->id_ndx].gid;
 
 		if (S_ISREG(file->mode) && file->length != st->st_size)
 			iflags |= ITEM_REPORT_SIZE;
@@ -348,10 +353,10 @@ void itemize(struct file_struct *file, i
 			iflags |= ITEM_REPORT_TIME;
 		if ((file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS))
 			iflags |= ITEM_REPORT_PERMS;
-		if (preserve_uid && am_root && file->uid != st->st_uid)
+		if (preserve_uid && am_root && uid != st->st_uid)
 			iflags |= ITEM_REPORT_OWNER;
-		if (preserve_gid && file->gid != GID_NONE
-		    && st->st_gid != file->gid)
+		if (preserve_gid && gid != GID_NONE
+		    && st->st_gid != gid)
 			iflags |= ITEM_REPORT_GROUP;
 	} else
 		iflags |= ITEM_IS_NEW;
--- old/log.c
+++ new/log.c
@@ -45,6 +45,7 @@ extern char *auth_user;
 extern char *log_format;
 extern char *logfile_format;
 extern char *logfile_name;
+extern struct id_pair *id_pairs;
 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
 extern iconv_t ic_chck;
 #endif
@@ -464,16 +465,16 @@ static void log_formatted(enum logcode c
 		case 'U':
 			strlcat(fmt, "ld", sizeof fmt);
 			snprintf(buf2, sizeof buf2, fmt,
-				 (long)file->uid);
+				 (long)id_pairs[file->id_ndx].uid);
 			n = buf2;
 			break;
 		case 'G':
-			if (file->gid == GID_NONE)
+			if (id_pairs[file->id_ndx].gid == GID_NONE)
 				n = "DEFAULT";
 			else {
 				strlcat(fmt, "ld", sizeof fmt);
 				snprintf(buf2, sizeof buf2, fmt,
-					 (long)file->gid);
+					 (long)id_pairs[file->id_ndx].gid);
 				n = buf2;
 			}
 			break;
--- old/rsync.c
+++ new/rsync.c
@@ -50,6 +50,7 @@ extern int keep_dirlinks;
 extern int make_backups;
 extern mode_t orig_umask;
 extern struct stats stats;
+extern struct id_pair *id_pairs;
 extern struct chmod_mode_struct *daemon_chmod_modes;
 
 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
@@ -128,6 +129,8 @@ int set_file_attrs(char *fname, struct f
 	int updated = 0;
 	STRUCT_STAT st2;
 	int change_uid, change_gid;
+	uid_t uid;
+	gid_t gid;
 
 	if (!st) {
 		if (dry_run)
@@ -160,9 +163,11 @@ int set_file_attrs(char *fname, struct f
 			updated = 1;
 	}
 
-	change_uid = am_root && preserve_uid && st->st_uid != file->uid;
-	change_gid = preserve_gid && file->gid != GID_NONE
-		&& st->st_gid != file->gid;
+	uid = id_pairs[file->id_ndx].uid;
+	gid = id_pairs[file->id_ndx].gid;
+	change_uid = am_root && preserve_uid && st->st_uid != uid;
+	change_gid = preserve_gid && gid != GID_NONE
+		&& st->st_gid != gid;
 #if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK
 	if (S_ISLNK(st->st_mode))
 		;
@@ -174,18 +179,18 @@ int set_file_attrs(char *fname, struct f
 				rprintf(FINFO,
 					"set uid of %s from %ld to %ld\n",
 					fname,
-					(long)st->st_uid, (long)file->uid);
+					(long)st->st_uid, (long)uid);
 			}
 			if (change_gid) {
 				rprintf(FINFO,
 					"set gid of %s from %ld to %ld\n",
 					fname,
-					(long)st->st_gid, (long)file->gid);
+					(long)st->st_gid, (long)gid);
 			}
 		}
 		if (do_lchown(fname,
-		    change_uid ? file->uid : st->st_uid,
-		    change_gid ? file->gid : st->st_gid) != 0) {
+		    change_uid ? uid : st->st_uid,
+		    change_gid ? gid : st->st_gid) != 0) {
 			/* shouldn't have attempted to change uid or gid
 			 * unless have the privilege */
 			rsyserr(FERROR, errno, "%s %s failed",
--- old/rsync.h
+++ new/rsync.h
@@ -496,6 +496,11 @@ struct hlink {
 	int hlindex;
 };
 
+struct id_pair {
+	uid_t uid;
+	gid_t gid;
+};
+
 #define F_DEV	link_u.idev->dev
 #define F_INODE	link_u.idev->inode
 
@@ -520,8 +525,7 @@ struct file_struct {
 		struct hlink *links;
 	} link_u;
 	time_t modtime;
-	uid_t uid;
-	gid_t gid;
+	int id_ndx;
 	mode_t mode;
 	uchar flags;	/* this item MUST remain last */
 };
--- old/uidlist.c
+++ new/uidlist.c
@@ -38,6 +38,8 @@ extern int preserve_gid;
 extern int numeric_ids;
 extern int am_root;
 
+struct id_pair *id_pairs;
+
 struct idlist {
 	struct idlist *next;
 	int id, id2;
@@ -47,6 +49,8 @@ struct idlist {
 static struct idlist *uidlist;
 static struct idlist *gidlist;
 
+static int pair_cnt = 0, pair_alloc = 0;
+
 static struct idlist *add_to_list(struct idlist **root, int id, char *name,
 				  int id2)
 {
@@ -308,7 +312,7 @@ void send_uid_list(int f)
 
 /* recv a complete uid/gid mapping from the peer and map the uid/gid
  * in the file list to local names */
-void recv_uid_list(int f, struct file_list *flist)
+void recv_uid_list(int f)
 {
 	int id, i;
 	char *name;
@@ -339,11 +343,40 @@ void recv_uid_list(int f, struct file_li
 
 	/* Now convert all the uids/gids from sender values to our values. */
 	if (am_root && preserve_uid && !numeric_ids) {
-		for (i = 0; i < flist->count; i++)
-			flist->files[i]->uid = match_uid(flist->files[i]->uid);
+		for (i = 0; i < pair_cnt; i++)
+			id_pairs[i].uid = match_uid(id_pairs[i].uid);
 	}
 	if (preserve_gid && (!am_root || !numeric_ids)) {
-		for (i = 0; i < flist->count; i++)
-			flist->files[i]->gid = match_gid(flist->files[i]->gid);
+		for (i = 0; i < pair_cnt; i++)
+			id_pairs[i].gid = match_gid(id_pairs[i].gid);
 	}
 }
+
+int id_pair(uid_t uid, gid_t gid)
+{
+	static int j = 0;
+
+	if (pair_cnt) {
+		int start = j;
+		/* We start our search where we left off because
+		 * the IDs usually come in clumps. */
+		do {
+			if (uid == id_pairs[j].uid && gid == id_pairs[j].gid)
+				return j;
+			if (++j == pair_cnt)
+				j = 0;
+		} while (j != start);
+	}
+
+	if (pair_cnt == pair_alloc) {
+		pair_alloc += 128;
+		id_pairs = realloc_array(id_pairs, struct id_pair,
+					  pair_alloc);
+	}
+
+	j = pair_cnt++;
+	id_pairs[j].uid = uid;
+	id_pairs[j].gid = gid;
+
+	return j;
+}
