diff mbox

[2/6] Add base64 encoder/decoder

Message ID 6ffbd56b9b714a41c021499a50ecac8e7a8e022b.1314370059.git.jan.kiszka@siemens.com
State New
Headers show

Commit Message

Jan Kiszka Aug. 26, 2011, 2:48 p.m. UTC
Will be used by QBuffer.

CC: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 Makefile.objs |    2 +-
 base64.c      |  202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 base64.h      |   19 ++++++
 3 files changed, 222 insertions(+), 1 deletions(-)
 create mode 100644 base64.c
 create mode 100644 base64.h

Comments

Peter Maydell Aug. 26, 2011, 3:21 p.m. UTC | #1
On 26 August 2011 15:48, Jan Kiszka <jan.kiszka@siemens.com> wrote:
> Will be used by QBuffer.

Is it possible to use the glib base64 encode/decode routines instead
of rolling our own here?

-- PMM
Jan Kiszka Aug. 26, 2011, 3:23 p.m. UTC | #2
On 2011-08-26 17:21, Peter Maydell wrote:
> On 26 August 2011 15:48, Jan Kiszka <jan.kiszka@siemens.com> wrote:
>> Will be used by QBuffer.
> 
> Is it possible to use the glib base64 encode/decode routines instead
> of rolling our own here?

Yeah, times are changing. Need to check what's there and how to use it.

Jan
Jan Kiszka Aug. 26, 2011, 3:47 p.m. UTC | #3
On 2011-08-26 17:23, Jan Kiszka wrote:
> On 2011-08-26 17:21, Peter Maydell wrote:
>> On 26 August 2011 15:48, Jan Kiszka <jan.kiszka@siemens.com> wrote:
>>> Will be used by QBuffer.
>>
>> Is it possible to use the glib base64 encode/decode routines instead
>> of rolling our own here?
> 
> Yeah, times are changing. Need to check what's there and how to use it.

Requires glib >= 2.12, we are currently at >= 2.0, right? Would it be OK
to raise the entry barrier?

I'm also still looking for error handling of g_base64_decode. I guess
one is supposed to compare returned length against some expected value.
Well, it's glib...

Jan
Jan Kiszka Aug. 26, 2011, 6:02 p.m. UTC | #4
On 2011-08-26 17:47, Jan Kiszka wrote:
> On 2011-08-26 17:23, Jan Kiszka wrote:
>> On 2011-08-26 17:21, Peter Maydell wrote:
>>> On 26 August 2011 15:48, Jan Kiszka <jan.kiszka@siemens.com> wrote:
>>>> Will be used by QBuffer.
>>>
>>> Is it possible to use the glib base64 encode/decode routines instead
>>> of rolling our own here?
>>
>> Yeah, times are changing. Need to check what's there and how to use it.
> 
> Requires glib >= 2.12, we are currently at >= 2.0, right? Would it be OK
> to raise the entry barrier?
> 
> I'm also still looking for error handling of g_base64_decode. I guess
> one is supposed to compare returned length against some expected value.
> Well, it's glib...

The length check is not sufficient, and glib's decoder fails on my
invalid input string tests.

I think proper error detection in base64 inputs is a worthwhile feature
that glib lacks. So I'll stick with roll-our-own (actually, it's
Mozilla's version).

Jan
Luiz Capitulino Sept. 2, 2011, 5:22 p.m. UTC | #5
On Fri, 26 Aug 2011 20:02:37 +0200
Jan Kiszka <jan.kiszka@siemens.com> wrote:

> On 2011-08-26 17:47, Jan Kiszka wrote:
> > On 2011-08-26 17:23, Jan Kiszka wrote:
> >> On 2011-08-26 17:21, Peter Maydell wrote:
> >>> On 26 August 2011 15:48, Jan Kiszka <jan.kiszka@siemens.com> wrote:
> >>>> Will be used by QBuffer.
> >>>
> >>> Is it possible to use the glib base64 encode/decode routines instead
> >>> of rolling our own here?
> >>
> >> Yeah, times are changing. Need to check what's there and how to use it.
> > 
> > Requires glib >= 2.12, we are currently at >= 2.0, right? Would it be OK
> > to raise the entry barrier?
> > 
> > I'm also still looking for error handling of g_base64_decode. I guess
> > one is supposed to compare returned length against some expected value.
> > Well, it's glib...
> 
> The length check is not sufficient, and glib's decoder fails on my
> invalid input string tests.
> 
> I think proper error detection in base64 inputs is a worthwhile feature
> that glib lacks. So I'll stick with roll-our-own (actually, it's
> Mozilla's version).

The guest-agent is using glib's to implement file read & write, I think
it should switch to Jan's implementation?
Gerd Hoffmann Sept. 5, 2011, 1:55 p.m. UTC | #6
On 08/26/11 17:47, Jan Kiszka wrote:
> On 2011-08-26 17:23, Jan Kiszka wrote:

>> [ using glib base64 decoder ]
>
> Requires glib>= 2.12, we are currently at>= 2.0, right? Would it be OK
> to raise the entry barrier?

In master it currently is >= 2.20 due to v9fs_init_worker_threads using 
g_thread_get_initialized which was added in 2.20 according to the docs. 
  Which makes the build fail on rhel-5 (shipping with glib 2.12).

Guess we'll need to clearly define which minimum glib version we want 
require.  And whenever we want apply this to the whole code base or 
allow different minimum requirements for different components.

Requiring glib 2.20 for all components isn't going to fly as we 
certainly want be able to run the qemu guest agent almost everythere. 
Requiring something newer than 2.0 for the qemu emulator might be 
reasonable of there are good reasons (aka useful glib features) though.

Comments?

cheers,
   Gerd
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index d1f3e5d..41febf6 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,7 +2,7 @@ 
 # QObject
 qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
 qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
-qobject-obj-y += qerror.o error.o
+qobject-obj-y += qerror.o error.o base64.o
 
 #######################################################################
 # oslib-obj-y is code depending on the OS (win32 vs posix)
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..f24d039
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,202 @@ 
+/*
+ * Base64 encoder/decoder conforming to RFC 4648
+ * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <inttypes.h>
+#include "base64.h"
+
+static const char base[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void encode3to4(const uint8_t *src, char *dest)
+{
+    uint32_t b32 = 0;
+    int i, j = 18;
+
+    for (i = 0; i < 3; i++) {
+        b32 <<= 8;
+        b32 |= src[i];
+    }
+    for (i = 0; i < 4; i++) {
+        dest[i] = base[(b32 >> j) & 0x3F];
+        j -= 6;
+    }
+}
+
+static void encode2to4(const uint8_t *src, char *dest)
+{
+    dest[0] = base[(src[0] >> 2) & 0x3F];
+    dest[1] = base[((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)];
+    dest[2] = base[(src[1] & 0x0F) << 2];
+    dest[3] = '=';
+}
+
+static void encode1to4(const uint8_t *src, char *dest)
+{
+    dest[0] = base[(src[0] >> 2) & 0x3F];
+    dest[1] = base[(src[0] & 0x03) << 4];
+    dest[2] = '=';
+    dest[3] = '=';
+}
+
+/*
+ * Encode data in 'src' of length 'srclen' to a base64 string, saving the
+ * null-terminated result in 'dest'. The size of the destition buffer must be
+ * at least ((srclen + 2) / 3) * 4 + 1.
+ */
+void base64_encode(const uint8_t *src, size_t srclen, char *dest)
+{
+    while (srclen >= 3) {
+        encode3to4(src, dest);
+        src += 3;
+        dest += 4;
+        srclen -= 3;
+    }
+    switch (srclen) {
+    case 2:
+        encode2to4(src, dest);
+        dest += 4;
+        break;
+    case 1:
+        encode1to4(src, dest);
+        dest += 4;
+        break;
+    case 0:
+        break;
+    }
+    dest[0] = 0;
+}
+
+static int32_t codetovalue(char c)
+{
+    if (c >= 'A' && c <= 'Z') {
+        return c - 'A';
+    } else if (c >= 'a' && c <= 'z') {
+        return c - 'a' + 26;
+    } else if (c >= '0' && c <= '9') {
+        return c - '0' + 52;
+    } else if (c == '+') {
+        return 62;
+    } else if (c == '/') {
+        return 63;
+    } else {
+        return -1;
+    }
+}
+
+static int decode4to3(const char *src, uint8_t *dest)
+{
+    uint32_t b32 = 0;
+    int32_t bits;
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        bits = codetovalue(src[i]);
+        if (bits < 0) {
+            return bits;
+        }
+        b32 <<= 6;
+        b32 |= bits;
+    }
+    dest[0] = (b32 >> 16) & 0xFF;
+    dest[1] = (b32 >> 8) & 0xFF;
+    dest[2] = b32 & 0xFF;
+
+    return 0;
+}
+
+static int decode3to2(const char *src, uint8_t *dest)
+{
+    uint32_t b32 = 0;
+    int32_t bits;
+
+    bits = codetovalue(src[0]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 = (uint32_t)bits;
+    b32 <<= 6;
+
+    bits = codetovalue(src[1]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 |= (uint32_t)bits;
+    b32 <<= 4;
+
+    bits = codetovalue(src[2]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 |= ((uint32_t)bits) >> 2;
+
+    dest[0] = (b32 >> 8) & 0xFF;
+    dest[1] = b32 & 0xFF;
+
+    return 0;
+}
+
+static int decode2to1(const char *src, uint8_t *dest)
+{
+    uint32_t b32;
+    int32_t bits;
+
+    bits = codetovalue(src[0]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 = (uint32_t)bits << 2;
+
+    bits = codetovalue(src[1]);
+    if (bits < 0) {
+        return bits;
+    }
+    b32 |= ((uint32_t)bits) >> 4;
+
+    dest[0] = b32;
+
+    return 0;
+}
+
+/*
+ * Convert string 'src' of length 'srclen' from base64 to binary form,
+ * saving the result in 'dest'. The size of the destination buffer must be at
+ * least srclen * 3 / 4.
+ *
+ * Returns 0 on success, -1 on conversion error.
+ */
+int base64_decode(const char *src, size_t srclen, uint8_t *dest)
+{
+    int ret;
+
+    while (srclen >= 4) {
+        ret = decode4to3(src, dest);
+        if (ret < 0) {
+            return ret;
+        }
+        src += 4;
+        dest += 3;
+        srclen -= 4;
+    }
+
+    switch (srclen) {
+    case 3:
+        return decode3to2(src, dest);
+    case 2:
+        return decode2to1(src, dest);
+    case 1:
+        return -1;
+    default: /* 0 */
+        return 0;
+    }
+}
diff --git a/base64.h b/base64.h
new file mode 100644
index 0000000..07e72a8
--- /dev/null
+++ b/base64.h
@@ -0,0 +1,19 @@ 
+/*
+ * Base64 encoder/decoder conforming to RFC 4648
+ * (based on Mozilla's nsprpub/lib/libc/src/base64.c)
+ *
+ * Copyright (C) 2010 Siemens AG
+ *
+ * Authors:
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <inttypes.h>
+#include <stddef.h>
+
+void base64_encode(const uint8_t *src, size_t srclen, char *dest);
+int base64_decode(const char *src, size_t srclen, uint8_t *dest);