Index: src/server/gdb_server.c
===================================================================
--- src/server/gdb_server.c	(revision 880)
+++ src/server/gdb_server.c	(working copy)
@@ -75,6 +75,9 @@
  * see the code in gdb_read_memory_packet() for further explanations */
 int gdb_report_data_abort = 0;
 
+/* Do not allocate this on the stack */
+char gdb_packet_buffer[GDB_BUFFER_SIZE];
+
 int gdb_last_signal(target_t *target)
 {
 	switch (target->debug_reason)
@@ -270,72 +273,78 @@
 	return ERROR_SERVER_REMOTE_CLOSED;
 }
 
+/* Look for a sequence of characters which can be run-length encoded.
+ * If there are any, update *CSUM and *P.  Otherwise, output the
+ * single character.  Return the number of characters consumed.  */
+
+static int gdb_try_rle (char *buf, int remaining, unsigned char *csum, char **p)
+{
+	int n;
+
+	/* Always output the character. */
+	*csum += buf[0];
+	*(*p)++ = buf[0];
+
+	/* Don't go past '~'. */
+	if (remaining > 97)
+		remaining = 97;
+
+	for (n = 1; n < remaining; n++)
+		if (buf[n] != buf[0])
+			break;
+	
+	/* N is the index of the first character not the same as buf[0].
+	 * buf[0] is counted twice, so by decrementing N, we get the number
+	 * of characters the RLE sequence will replace. */
+	n--;
+
+	if (n < 3)
+		return 1;
+
+	/* Skip the frame characters.  The manual says to skip '+' and '-'
+	 * also, but there's no reason to.  Unfortunately these two unusable
+	 * characters double the encoded length of a four byte zero value. */
+	while (n + 29 == '$' || n + 29 == '#')
+		n--;
+	
+	*csum += '*';
+	*(*p)++ = '*';
+	*csum += n + 29;
+	*(*p)++ = n + 29;
+	
+	return n + 1;
+}
+
 int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
 {
 	int i;
 	unsigned char my_checksum = 0;
-#ifdef _DEBUG_GDB_IO_
-	char *debug_buffer;
-#endif
 	int reply;
 	int retval;
 	gdb_connection_t *gdb_con = connection->priv;
-
-	for (i = 0; i < len; i++)
-		my_checksum += buffer[i];
-
-#ifdef _DEBUG_GDB_IO_
-	/* 
-	 * At this point we should have nothing in the input queue from GDB,
-	 * however sometimes '-' is sent even though we've already received
-	 * an ACK (+) for everything we've sent off.
-	 */
-	int gotdata;
-	for (;;)
-	{
-		if ((retval=check_pending(connection, 0, &gotdata))!=ERROR_OK)
-			return retval;
-		if (!gotdata)
-			break;
-		if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
-			return retval;
-		LOG_WARNING("Discard unexpected char %c", reply);
-	}
-#endif
-
+	char *p;
+	
+	p = &gdb_packet_buffer[0];
+	*p++ = '$';
+	
+	/* first attempt to run length encode (RLE) packet */
+	for (i = 0; i < len;)
+		i += gdb_try_rle(buffer + i, len - i, &my_checksum, &p);
+	
+	/* append checksum to packet */
+	*p++ = '#';
+	*p++ = DIGITS[(my_checksum >> 4) & 0xf];
+	*p++ = DIGITS[my_checksum & 0xf];
+	*p = '\0';
+	
 	while (1)
 	{
 #ifdef _DEBUG_GDB_IO_
-		debug_buffer = malloc(len + 1);
-		memcpy(debug_buffer, buffer, len);
-		debug_buffer[len] = 0;
-		LOG_DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);
-		free(debug_buffer);
+		LOG_DEBUG("sending packet '%s'", gdb_packet_buffer);
 #endif
+		
+		gdb_write(connection, gdb_packet_buffer, p - gdb_packet_buffer);
 
-		char local_buffer[1024];
-		local_buffer[0] = '$';
-		if (len+4 <= sizeof(local_buffer))
-		{
-			/* performance gain on smaller packets by only a single call to gdb_write() */
-			memcpy(local_buffer+1, buffer, len++);
-			local_buffer[len++] = '#';
-			local_buffer[len++] = DIGITS[(my_checksum >> 4) & 0xf];
-			local_buffer[len++] = DIGITS[my_checksum & 0xf];
-			gdb_write(connection, local_buffer, len);
-		}
-		else
-		{
-			/* larger packets are transmitted directly from caller supplied buffer
-			   by several calls to gdb_write() to avoid dynamic allocation */
-			local_buffer[1] = '#';
-			local_buffer[2] = DIGITS[(my_checksum >> 4) & 0xf];
-			local_buffer[3] = DIGITS[my_checksum & 0xf];
-			gdb_write(connection, local_buffer, 1);
-			gdb_write(connection, buffer, len);
-			gdb_write(connection, local_buffer+1, 3);
-		}
-
 		if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
 			return retval;
 
@@ -1848,9 +1857,6 @@
 	gdb_output_con(connection, string);
 }
 
-/* Do not allocate this on the stack */
-char gdb_packet_buffer[GDB_BUFFER_SIZE];
-
 int gdb_input_inner(connection_t *connection)
 {
 	gdb_service_t *gdb_service = connection->service->priv;

