[m-rev.] diff: add base64 encoding lib to extras
Peter Ross
pro at missioncriticalit.com
Fri Nov 10 12:59:06 AEDT 2006
Hi,
I've started to move the libraries developed at MC into extras.
The plan is to just check them in, and if you guys want to change
the coding standards, etc., etc., feel free.
===================================================================
Estimated hours taken: 1
Branches: main
extras/README:
extras/base64/base64.m:
Add routines for encoding and decoding base64 strings.
Index: extras/README
===================================================================
RCS file: /home/mercury1/repository/mercury/extras/README,v
retrieving revision 1.18
diff -U5 -r1.18 README
--- extras/README 7 Sep 2006 08:32:19 -0000 1.18
+++ extras/README 10 Nov 2006 01:45:29 -0000
@@ -3,10 +3,12 @@
Most of these can be built by running the commands `mmake depend' and
then `mmake' in the relevant subdirectory, and many can be installed by
running `mmake install'.
+base64 A library for base64 encoding and decoding.
+
cgi A couple of Mercury library modules for doing HTML forms
programming using CGI (Common Gateway Interface).
complex_numbers
A Mercury library package containing support for
New File: extras/base64/base64.m
===================================================================
%------------------------------------------------------------------------------%
% base64.m
% Michel Dehennin <mdh at missioncriticalit.com>
%
% Original C code is public domain code provided by TeX User Group
% http://www.tug.org/ftp/vm/base64-decode.c
% http://www.tug.org/ftp/vm/base64-encode.c
%
% This code is in the public domain.
%
%------------------------------------------------------------------------------%
:- module base64.
:- interface.
:- import_module list.
%
% Encode a string in base64.
%
:- pred encode64(string::in, string::out) is det.
%
% Encodes a list of bytes as a base 64 string.
% Note this means that the integers must be in the range 0-255.
% This constrain is not checked.
%
:- pred encode64_byte_list(list(int)::in, string::out) is det.
%
% Decodes a base64 string to clear text.
% WARNING: If the resulting string contains non-terminating null characters,
% as in a PDF file for instance, the string is likely to be truncated
%
:- pred decode64(string::in, string::out) is det.
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
:- implementation.
:- import_module bool, char, int, float, string.
:- import_module list, map, set.
:- import_module exception, require, std_util.
encode64(Data, Base64) :-
encode64(Data, string.length(Data), Base64).
:- pred encode64(string::in, int::in, string::out) is det.
:- pragma foreign_decl(c, local, "
#include <string.h>
unsigned char alphabet[64] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";
").
:- pragma foreign_proc(c,
encode64(Data::in, Len::in, Base64::out),
[will_not_call_mercury, thread_safe, promise_pure], "
{
MR_Word base64_buff;
char *base64_ptr;
int cols, bits, c, char_count;
int i;
/*
* Base64 encoded data uses 4:3 times more space than the original string
* We need to foresee an extra MR_Word for the string terminator character
*/
MR_offset_incr_hp_atomic(base64_buff, 0, ((((Len + 2) / 3) * 4) + sizeof(MR_Word)) / sizeof(MR_Word));
base64_ptr = (char *) base64_buff;
char_count = 0;
bits = 0;
cols = 0;
for(i = 0; i < Len; i++) {
/* need to cast to an unsigned char otherwise we might get negative values */
c = (unsigned char) Data[i];
bits += c;
char_count++;
if (char_count == 3) {
*base64_ptr++ = alphabet[bits >> 18];
*base64_ptr++ = alphabet[(bits >> 12) & 0x3f];
*base64_ptr++ = alphabet[(bits >> 6) & 0x3f];
*base64_ptr++ = alphabet[bits & 0x3f];
/* Invalidates the size of allocated memory */
/*
cols += 4;
if (cols == 72) {
putchar('\n');
cols = 0;
}
*/
bits = 0;
char_count = 0;
} else {
bits <<= 8;
}
}
if (char_count != 0) {
bits <<= 16 - (8 * char_count);
*base64_ptr++ = alphabet[bits >> 18];
*base64_ptr++ = alphabet[(bits >> 12) & 0x3f];
if (char_count == 1) {
*base64_ptr++ = '=';
*base64_ptr++ = '=';
} else {
*base64_ptr++ = alphabet[(bits >> 6) & 0x3f];
*base64_ptr++ = '=';
}
/*
if (cols > 0)
putchar('\n');
*/
}
*base64_ptr = '\\0';
Base64 = (char *) base64_buff;
}
").
:- pragma foreign_proc(c,
decode64(Base64::in, Data::out),
[will_not_call_mercury, thread_safe, promise_pure], "
{
MR_Word data_buff;
char *data_ptr;
static char inalphabet[256], decoder[256];
int i, bits, c, char_count, errors = 0;
/*
* Decoded data uses 3:4 of the space of the Base64 string
*/
MR_offset_incr_hp_atomic(data_buff, 0, (((strlen(Base64) * 3) / 4) + sizeof(MR_Word)) / sizeof(MR_Word));
data_ptr = (char *) data_buff;
for (i = (sizeof alphabet) - 1; i >= 0 ; i--) {
inalphabet[alphabet[i]] = 1;
decoder[alphabet[i]] = i;
}
char_count = 0;
bits = 0;
for(i = 0; i < strlen(Base64); i++) {
/* need to cast to an unsigned char otherwise we might get negative values */
c = (unsigned char) Base64[i];
if (c == '=')
break;
/* Skip invalid characters */
if (c > 255 || ! inalphabet[c])
continue;
bits += decoder[c];
char_count++;
if (char_count == 4) {
*data_ptr++ = (bits >> 16);
*data_ptr++ = ((bits >> 8) & 0xff);
*data_ptr++ = (bits & 0xff);
bits = 0;
char_count = 0;
} else {
bits <<= 6;
}
}
if (i == strlen(Base64)) {
if (char_count) {
fprintf(stderr, \"base64 encoding incomplete: at least %d bits truncated\",
((4 - char_count) * 6));
errors++;
}
} else { /* c == '=' */
switch (char_count) {
case 1:
fprintf(stderr, \"base64 encoding incomplete: at least 2 bits missing\");
errors++;
break;
case 2:
*data_ptr++ = (bits >> 10);
break;
case 3:
*data_ptr++ = (bits >> 16);
*data_ptr++ = ((bits >> 8) & 0xff);
break;
}
}
*data_ptr = '\\0';
Data = (char *) data_buff;
}
").
%
% We define our own version of map/2 so that we can
% apply the --optimize-constructor-last-call optimization
% (this optimization is turned off for the standard
% library by default).
%
:- func base64.map(func(X) = Y, list(X)) = list(Y).
base64.map(_, []) = [].
base64.map(F, [H0 | T0]) = [H | T] :-
H = F(H0),
T = base64.map(F, T0).
encode64_byte_list(Bytes, Base64) :-
Chars = base64.map(char.det_from_int, Bytes),
Length = list.length(Chars),
String = string.from_char_list(Chars),
encode64(String, Length, Base64).
:- end_module base64.
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et tw=0 wm=0
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list