From e421c602b4ae27e69fee058c30b5bb7e1ac17c99 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Mon, 4 Feb 2013 14:31:20 -0500 Subject: [PATCH] lib: adds base64 module and according tests MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Implements the base64 encode and decode algorithms in Nit. Signed-off-by: Alexis Laferrière --- lib/base64.nit | 136 +++++++++++++++++++++++++++++++++++++++++++++ tests/sav/test_base64.sav | 14 +++++ tests/test_base64.nit | 33 +++++++++++ 3 files changed, 183 insertions(+) create mode 100644 lib/base64.nit create mode 100644 tests/sav/test_base64.sav create mode 100644 tests/test_base64.nit diff --git a/lib/base64.nit b/lib/base64.nit new file mode 100644 index 0000000..8a718eb --- /dev/null +++ b/lib/base64.nit @@ -0,0 +1,136 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2013 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Offers the base 64 encoding and decoding algorithms +module base64 + +redef class String + + # Alphabet used by the base64 algorithm + private fun base64_chars : String + do + return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + end + private fun inverted_base64_chars : HashMap[Char,Int] + do + var inv_base64_chars = new HashMap[Char,Int] + for k in [0..base64_chars.length[ do + inv_base64_chars[ base64_chars[k] ] = k + end + return inv_base64_chars + end + + # Encodes the receiver string to base64. + # By default, uses "=" for padding. + fun encode_base64 : String do return encode_base64_custom_padding( '=' ) + fun encode_base64_custom_padding( padding : Char ) : String + do + var base64_chars = once base64_chars + var length = length + + var steps = length / 3 + var chars_in_last_step = length % 3 + var result_length = steps*4 + if chars_in_last_step > 0 then result_length += 4 + var result = (padding.to_s*result_length).to_cstring + + var mask_6bit = 63 + + for s in [0..steps[ do + var e = 0 + for ss in [0..3[ do + e += self[s*3+ss].ascii.lshift((2-ss)*8) + end + for ss in [0..4[ do + result[s*4+3-ss] = base64_chars[ e.rshift(ss*6).bin_and( mask_6bit ) ] + end + end + + if chars_in_last_step == 1 then + var e = self[length-1].ascii.lshift(16) + for ss in [0..2[ do + result[steps*4+1-ss] = base64_chars[ e.rshift((ss+2)*6).bin_and( mask_6bit ) ] + end + else if chars_in_last_step == 2 then + var e = self[length-2].ascii.lshift(16) + + self[length-1].ascii.lshift(8) + for ss in [0..3[ do + result[steps*4+2-ss] = base64_chars[ e.rshift((ss+1)*6).bin_and( mask_6bit ) ] + end + end + + return new String.from_cstring( result ) + end + + # Decodes the receiver string from base64. + # By default, uses "=" for padding. + fun decode_base64 : String do return decode_base64_custom_padding( '=' ) + fun decode_base64_custom_padding( padding : Char ) : String + do + var inverted_base64_chars = once inverted_base64_chars + var length = length + assert length % 4 == 0 else print "base64::decode_base64 only supports strings of length multiple of 4" + + var steps = length / 4 + var result_length = steps*3 + + var padding_begin = padding.search_index_in( self, 0 ) + var padding_count : Int + if padding_begin == -1 then + padding_count = 0 + else + padding_count = length - padding_begin + steps -= 1 + result_length -= padding_count + end + + var result = ("#"*result_length).to_cstring + + var mask_8bit = 255 + + for s in [0..steps[ do + var e = 0 + for ss in [0..4[ do + e += inverted_base64_chars[self[s*4+ss]].lshift((3-ss)*6) + end + + for ss in [0..3[ do + result[s*3+ss] = e.rshift((2-ss)*8).bin_and( mask_8bit ).ascii + end + end + + var s = steps + if padding_count == 1 then + var e = 0 + for ss in [0..3[ do + e += inverted_base64_chars[self[s*4+ss]].lshift((3-ss)*6) + end + + for ss in [0..2[ do + result[s*3+ss] = e.rshift((2-ss)*8).bin_and( mask_8bit ).ascii + end + else if padding_count == 2 then + var e = 0 + for ss in [0..2[ do + e += inverted_base64_chars[self[s*4+ss]].lshift((3-ss)*6) + end + + result[s*3] = e.rshift(2*8).bin_and( mask_8bit ).ascii + end + + return new String.from_cstring( result ) + end +end diff --git a/tests/sav/test_base64.sav b/tests/sav/test_base64.sav new file mode 100644 index 0000000..01698e8 --- /dev/null +++ b/tests/sav/test_base64.sav @@ -0,0 +1,14 @@ +: +f: Zg== +fo: Zm8= +foo: Zm9v +foob: Zm9vYg== +fooba: Zm9vYmE= +foobar: Zm9vYmFy +: +Zg==: f +Zm8=: fo +Zm9v: foo +Zm9vYg==: foob +Zm9vYmE=: fooba +Zm9vYmFy: foobar diff --git a/tests/test_base64.nit b/tests/test_base64.nit new file mode 100644 index 0000000..af82e47 --- /dev/null +++ b/tests/test_base64.nit @@ -0,0 +1,33 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2013 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 + +print ":" + "".encode_base64 +print "f: " + "f".encode_base64 +print "fo: " + "fo".encode_base64 +print "foo: " + "foo".encode_base64 +print "foob: " + "foob".encode_base64 +print "fooba: " + "fooba".encode_base64 +print "foobar: " + "foobar".encode_base64 + +print ":" + "".decode_base64 +print "Zg==: " + "Zg==".decode_base64 +print "Zm8=: " + "Zm8=".decode_base64 +print "Zm9v: " + "Zm9v".decode_base64 +print "Zm9vYg==: " + "Zm9vYg==".decode_base64 +print "Zm9vYmE=: " + "Zm9vYmE=".decode_base64 +print "Zm9vYmFy: " + "Zm9vYmFy".decode_base64 -- 1.7.9.5