summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--filter.c135
-rw-r--r--filter.h7
-rw-r--r--txr.186
3 files changed, 208 insertions, 20 deletions
diff --git a/filter.c b/filter.c
index 3f93a2ed..0493d566 100644
--- a/filter.c
+++ b/filter.c
@@ -47,6 +47,7 @@ val filter_k, lfilt_k, rfilt_k, tohtml_k, fromhtml_k;
val tohtml_star_k;
val upcase_k, downcase_k;
val topercent_k, frompercent_k, tourl_k, fromurl_k, tobase64_k, frombase64_k;
+val tobase64url_k, frombase64url_k;
val tonumber_k, toint_k, tofloat_k, hextoint_k;
static val make_trie(void)
@@ -740,11 +741,9 @@ INLINE void col_check(cnum *pcol, cnum wcol, val out)
}
}
-val base64_stream_enc(val out, val in, val nbytes, val wrap_cols)
+static val base64_stream_enc_impl(val out, val in, val nbytes, val wrap_cols,
+ const char *b64)
{
- static const char *b64 = {
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
- };
int ulim = nilp(default_null_arg(nbytes));
cnum col = 0;
cnum nb = if3(ulim, 0, c_num(nbytes));
@@ -802,6 +801,14 @@ val base64_stream_enc(val out, val in, val nbytes, val wrap_cols)
return plus(ret, num_fast(count));
}
+val base64_stream_enc(val out, val in, val nbytes, val wrap_cols)
+{
+ static const char *b64 = {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+ };
+ return base64_stream_enc_impl(out, in, nbytes, wrap_cols, b64);
+}
+
val base64_encode(val str, val wrap_cols)
{
val in = make_byte_input_stream(str);
@@ -812,6 +819,24 @@ val base64_encode(val str, val wrap_cols)
return get_string_from_stream(out);
}
+val base64url_stream_enc(val out, val in, val nbytes, val wrap_cols)
+{
+ static const char *b64 = {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
+ };
+ return base64_stream_enc_impl(out, in, nbytes, wrap_cols, b64);
+}
+
+val base64url_encode(val str, val wrap_cols)
+{
+ val in = make_byte_input_stream(str);
+ val out = make_string_output_stream();
+
+ (void) base64url_stream_enc(out, in, nil, wrap_cols);
+
+ return get_string_from_stream(out);
+}
+
INLINE cnum get_base64_char(val in)
{
for (;;) {
@@ -845,22 +870,24 @@ INLINE int b64_code(cnum c)
}
}
-val base64_stream_dec(val out, val in)
+static val base64_stream_dec_impl(val out, val in,
+ cnum (*get_char)(val),
+ int (*get_code)(cnum))
{
val ret = zero;
cnum count = 0;
for (;;) {
- cnum c0 = get_base64_char(in);
- cnum c1 = c0 ? get_base64_char(in) : 0;
- cnum c2 = c1 ? get_base64_char(in) : 0;
- cnum c3 = c2 ? get_base64_char(in) : 0;
+ cnum c0 = get_char(in);
+ cnum c1 = c0 ? get_char(in) : 0;
+ cnum c2 = c1 ? get_char(in) : 0;
+ cnum c3 = c2 ? get_char(in) : 0;
if (c3) {
- long f0 = b64_code(c0);
- long f1 = b64_code(c1);
- long f2 = b64_code(c2);
- long f3 = b64_code(c3);
+ long f0 = get_code(c0);
+ long f1 = get_code(c1);
+ long f2 = get_code(c2);
+ long f3 = get_code(c3);
long word = (f0 << 18) | (f1 << 12) | (f2 << 6) | f3;
put_byte(num_fast((word >> 16) ), out);
@@ -868,9 +895,9 @@ val base64_stream_dec(val out, val in)
put_byte(num_fast( word & 0xff), out);
count += 3;
} else if (c2) {
- long f0 = b64_code(c0);
- long f1 = b64_code(c1);
- long f2 = b64_code(c2);
+ long f0 = get_code(c0);
+ long f1 = get_code(c1);
+ long f2 = get_code(c2);
long word = (f0 << 18) | (f1 << 12) | (f2 << 6);
put_byte(num_fast((word >> 16) ), out);
@@ -878,8 +905,8 @@ val base64_stream_dec(val out, val in)
count += 2;
break;
} else if (c0 || c1) {
- long f0 = b64_code(c0);
- long f1 = b64_code(c1);
+ long f0 = get_code(c0);
+ long f1 = get_code(c1);
long word = (f0 << 18) | (f1 << 12);
put_byte(num_fast((word >> 16) ), out);
count += 1;
@@ -897,6 +924,11 @@ val base64_stream_dec(val out, val in)
return plus(ret, num_fast(count));
}
+val base64_stream_dec(val out, val in)
+{
+ return base64_stream_dec_impl(out, in, get_base64_char, b64_code);
+}
+
val base64_decode(val str)
{
val in = make_string_input_stream(str);
@@ -917,6 +949,64 @@ val base64_decode_buf(val str)
return get_buf_from_stream(out);
}
+INLINE cnum get_base64url_char(val in)
+{
+ for (;;) {
+ val ch = get_char(in);
+ if (!ch)
+ return 0;
+ if (chr_isalnum(ch) || ch == chr('-') || ch == chr('_'))
+ return c_chr(ch);
+ if (!chr_isspace(ch) && ch != chr('=')) {
+ unget_char(ch, in);
+ return 0;
+ }
+ }
+}
+
+INLINE int b64url_code(cnum c)
+{
+ if ('A' <= c && c <= 'Z')
+ return c - 'A';
+ if ('a' <= c && c <= 'z')
+ return c - 'a' + 26;
+ if ('0' <= c && c <= '9')
+ return c - '0' + 26 + 26;
+ switch (c) {
+ case '-':
+ return 62;
+ case '_':
+ return 63;
+ default:
+ return 0;
+ }
+}
+
+val base64url_stream_dec(val out, val in)
+{
+ return base64_stream_dec_impl(out, in, get_base64url_char, b64url_code);
+}
+
+val base64url_decode(val str)
+{
+ val in = make_string_input_stream(str);
+ val out = make_string_output_stream();
+
+ (void) base64url_stream_dec(out, in);
+
+ return get_string_from_stream(out);
+}
+
+val base64url_decode_buf(val str)
+{
+ val in = make_string_input_stream(str);
+ val out = make_buf_stream(nil);
+
+ (void) base64url_stream_dec(out, in);
+
+ return get_buf_from_stream(out);
+}
+
static val html_encode(val str)
{
return trie_filter_string(get_filter(tohtml_k), str);
@@ -952,6 +1042,8 @@ void filter_init(void)
tonumber_k = intern(lit("tonumber"), keyword_package);
tobase64_k = intern(lit("tobase64"), keyword_package);
frombase64_k = intern(lit("frombase64"), keyword_package);
+ tobase64url_k = intern(lit("tobase64url"), keyword_package);
+ frombase64url_k = intern(lit("frombase64url"), keyword_package);
toint_k = intern(lit("toint"), keyword_package);
tofloat_k = intern(lit("tofloat"), keyword_package);
hextoint_k = intern(lit("hextoint"), keyword_package);
@@ -978,6 +1070,8 @@ void filter_init(void)
sethash(fh, fromurl_k, curry_12_1(func_n2(url_decode), t));
sethash(fh, tobase64_k, curry_12_1(func_n2(base64_encode), 0));
sethash(fh, frombase64_k, func_n1(base64_decode));
+ sethash(fh, tobase64url_k, curry_12_1(func_n2(base64url_encode), 0));
+ sethash(fh, frombase64url_k, func_n1(base64url_decode));
sethash(fh, tonumber_k, func_n1(num_str));
sethash(fh, toint_k, curry_12_1(func_n2(int_str), nil));
sethash(fh, tofloat_k, func_n1(flo_str));
@@ -1000,6 +1094,11 @@ void filter_init(void)
reg_fun(intern(lit("base64-encode"), user_package), func_n2o(base64_encode, 1));
reg_fun(intern(lit("base64-decode"), user_package), func_n1(base64_decode));
reg_fun(intern(lit("base64-decode-buf"), user_package), func_n1(base64_decode_buf));
+ reg_fun(intern(lit("base64url-stream-enc"), user_package), func_n4o(base64url_stream_enc, 2));
+ reg_fun(intern(lit("base64url-stream-dec"), user_package), func_n2(base64url_stream_dec));
+ reg_fun(intern(lit("base64url-encode"), user_package), func_n2o(base64url_encode, 1));
+ reg_fun(intern(lit("base64url-decode"), user_package), func_n1(base64url_decode));
+ reg_fun(intern(lit("base64url-decode-buf"), user_package), func_n1(base64url_decode_buf));
reg_fun(intern(lit("html-encode"), user_package), func_n1(html_encode));
reg_fun(intern(lit("html-encode*"), user_package),
func_n1(html_encode_star));
diff --git a/filter.h b/filter.h
index 3d1df625..d4b37ea2 100644
--- a/filter.h
+++ b/filter.h
@@ -51,5 +51,12 @@ val base64_encode(val str, val wrap_cols);
val base64_decode(val str);
val base64_decode_buf(val str);
+val base64url_stream_enc(val out_stream, val in_stream, val nbytes, val wrap_cols);
+val base64url_stream_dec(val out_stream, val in_stream);
+
+val base64url_encode(val str, val wrap_cols);
+val base64url_decode(val str);
+val base64url_decode_buf(val str);
+
void filter_init(void);
diff --git a/txr.1 b/txr.1
index 39f555b3..9dd959e8 100644
--- a/txr.1
+++ b/txr.1
@@ -9454,10 +9454,27 @@ reserved set, encodes to
.codn %2B .
.coIP :frombase64
-Decode from the Base64 encoding described in RFC 4648.
+Decode from the Base 64 encoding described in RFC 4648, section 5.
.coIP :tobase64
-Encodes to the RFC 4648 Base64 encoding.
+Encode to the Base 64 encoding described in RFC 4648, section 5.
+
+.coIP :frombase64url
+Decode from the Base64 encoding described in RFC 4648, section 6.
+This uses the URL and filename safe alphabet, in which the
+.code +
+(plus) and
+.code /
+(slash) characters used in regular Base 64 are respectively replaced with
+.code -
+(minus) and
+.code _
+(underscore).
+
+.coIP :tobase64url
+Encode to the Base 64 encoding described in RFC 4648, section 6. See
+.code :frombase64url
+above.
.coIP :tonumber
Converts strings to numbers. Strings that contain a period,
@@ -62902,6 +62919,7 @@ function converts the UTF-8 representation of
or the contents of
.metn buf ,
to Base64 and returns that representation as a string.
+The Base64 encoding is described in RFC 4648, section 5.
The second argument must either be a character string, or
a buffer object.
@@ -62971,6 +62989,7 @@ The
and
.code base64-stream-dec
perform, respectively, bulk Base64 encoding and decoding between streams.
+This format is described in RFC 4648, section 5.
The
.meta in
@@ -63040,6 +63059,69 @@ the
.code base64-stream-dec
function returns the number of bytes decoded.
+.coNP Functions @, base64url-encode @ base64url-decode and @ base64url-decode-buf
+.synb
+.mets (base64url-encode >> [ string | << buf ] <> [ column-width ])
+.mets (base64url-decode < string)
+.mets (base64url-decode-buf < string)
+.syne
+.desc
+The
+.codn base64url-encode ,
+.code base64url-decode
+and
+.code base64url-decode-buf
+functions conform, in nearly every respect, to the descriptions of,
+respectively,
+.codn base64-encode ,
+.code base64-decode
+and
+.codn base64-decode-buf .
+The difference is that these functions use the encoding described in
+section 6 of RFC 4648, rather than section 5. This means that, in the
+encoding alphabet, instead of the symbols
+.code +
+(plus)
+and
+.code /
+(slash)
+the symbols
+.code -
+(minus)
+and
+.code _
+(underline) are used.
+
+.coNP Functions @ base64url-stream-enc and @ base64url-stream-dec
+.synb
+.mets (base64url-stream-enc < out < in >> [ nbytes <> [ column-width ]])
+.mets (base64url-stream-dec < out << in )
+.syne
+.desc
+The
+.code base64url-stream-enc
+and
+.code base64url-stream-dec
+functions conform, in nearly every respect, to the descriptions of,
+respectively,
+.code base64-stream-enc
+and
+.codn base64-stream-dec .
+The difference is that these functions use the encoding described in
+section 6 of RFC 4648, rather than section 5. This means that, in the
+encoding alphabet, instead of the symbols
+.code +
+(plus)
+and
+.code /
+(slash)
+the symbols
+.code -
+(minus)
+and
+.code _
+(underline) are used.
+
.SS* Filter Module
The filter module provides a trie (pronounced "try") data structure,
which is suitable for representing dictionaries for efficient filtering.