diff --git a/base64.c b/base64.c
index 4ec6c6af500ba2004a0a4bba8114cdbce50f5f08..7124060e9236707f7fc22ec400e3fe0a6e8776fe 100644
--- a/base64.c
+++ b/base64.c
@@ -26,6 +26,7 @@
 #include "base64.h"
 
 #include <assert.h>
+#include <stdlib.h>
 
 #define TABLE_INVALID -1
 #define TABLE_SPACE -2
@@ -56,42 +57,55 @@ static const signed char decode_table[256] =
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 };
 
+#define ENCODE(x) (encode_table[0x3F & (x)])
+
 unsigned 
 base64_encode(uint8_t *dst,
               unsigned src_length,
               const uint8_t *src)
 {
-  uint8_t *out = dst;
+  unsigned dst_length = BASE64_ENCODE_LENGTH(src_length);
+  const uint8_t *in = src + src_length;
+  uint8_t *out = dst + dst_length;
+  unsigned left_over = src_length % 3;
 
-  while (src_length >= 3)
+  if (left_over)
     {
-      *out++ = encode_table[0x3F &  (src[0] >> 2)];
-      *out++ = encode_table[0x3F & ((src[0] << 4) | (src[1] >> 4))];
-      *out++ = encode_table[0x3F & ((src[1] << 2) | (src[2] >> 6))];
-      *out++ = encode_table[0x3F &   src[2]];
-      src += 3;
-      src_length -= 3;
-    }
+      switch(left_over)
+	{
+	case 1:
+	  in--;
+	  *--out = '=';
+	  *--out = '=';
+	  *--out = ENCODE(in[0] << 4);
+	  *--out = ENCODE(in[0] >> 2);
+	  break;
+	  
+	case 2:
+	  in-= 2;
+	  *--out = '=';
+	  *--out = ENCODE( in[1] << 2);
+	  *--out = ENCODE((in[0] << 4) | (in[1] >> 4));
+	  *--out = ENCODE( in[0] >> 2);
+	  break;
 
-  switch (src_length) 
+	default:
+	  abort();
+	}
+    }
+  
+  while (in > src)
     {
-    case 2:
-      *out++ = encode_table[0x3F &  (src[0] >> 2)];
-      *out++ = encode_table[0x3F & ((src[0] << 4) | (src[1] >> 4))];
-      *out++ = encode_table[0x3F &  (src[1] << 2)];
-      *out++ = '=';
-      break;
-    case 1:
-      *out++ = encode_table[0x3F & (src[0] >> 2)];
-      *out++ = encode_table[0x3F & (src[0] << 4)];
-      *out++ = '=';
-      *out++ = '=';
-      break;
-    case 0:
-      break;
+      in -= 3;
+      *--out = ENCODE( in[2]);
+      *--out = ENCODE((in[1] << 2) | (in[2] >> 6));
+      *--out = ENCODE((in[0] << 4) | (in[1] >> 4));
+      *--out = ENCODE( in[0] >> 2);
     }
 
-  return out - dst;
+  assert(out == dst);
+
+  return dst_length;
 }
 
 void
diff --git a/base64.h b/base64.h
index e0f3c5f3c7eead5449d70f95e0126cdf4713b39f..d69c9ccdb72c2014ed601c412668066b69fe2357 100644
--- a/base64.h
+++ b/base64.h
@@ -33,6 +33,9 @@
 #define BASE64_BINARY_BLOCK_SIZE 3
 #define BASE64_TEXT_BLOCK_SIZE 4
 
+/* Overlapping source and destination is allowed, as long as the start
+ * of the source area is not later than the start of the destination
+ * area. */
 unsigned /* Returns the length of encoded data */
 base64_encode(uint8_t *dst,
               unsigned src_length,
@@ -53,6 +56,9 @@ struct base64_ctx /* Internal, do not modify */
 void
 base64_decode_init(struct base64_ctx *ctx);
 
+/* Overlapping source and destination is allowed, as long as the start
+ * of the source area is not before the start of the destination
+ * area. */
 unsigned /* Returns the length of decoded data */
 base64_decode_update(struct base64_ctx *ctx,
                      uint8_t *dst,