lib: Update sha1 and base64 to use Bytes instead of String
authorLucas Bajolet <r4pass@hotmail.com>
Mon, 14 Sep 2015 15:13:24 +0000 (11:13 -0400)
committerLucas Bajolet <r4pass@hotmail.com>
Tue, 15 Sep 2015 15:03:28 +0000 (11:03 -0400)
Signed-off-by: Lucas Bajolet <r4pass@hotmail.com>

contrib/opportunity/src/opportunity_controller.nit
contrib/opportunity/src/opportunity_model.nit
examples/rosettacode/sha_1.nit
lib/base64.nit
lib/core/bytes.nit
lib/sha1.nit
lib/websocket/websocket.nit

index 868dd9e..26a4ef6 100644 (file)
@@ -16,7 +16,6 @@
 module opportunity_controller
 
 import nitcorn
-import sha1
 import templates
 import opportunity_model
 
index ef7654d..cc72f23 100644 (file)
@@ -247,7 +247,7 @@ class Meetup
        redef fun commit(db) do
                if id == "" then
                        var time = get_time
-                       var tmpid = (name + date + place + time.to_s).sha1_to_s
+                       var tmpid = (name + date + place + time.to_s).sha1.hexdigest
                        if not db.execute("INSERT INTO meetups (id, name, date, place, answer_mode) VALUES({tmpid.to_sql_string}, {name.html_escape.to_sql_string}, {date.html_escape.to_sql_string}, {place.html_escape.to_sql_string}, {answer_mode});") then
                                print "Error recording entry Meetup {self}"
                                print db.error or else "Null error"
index 2eaf471..8776d48 100644 (file)
@@ -9,4 +9,4 @@ module sha_1
 
 import sha1
 
-print "Rosetta Code".sha1_to_s
+print "Rosetta Code".sha1.hexdigest
index 5f1c920..4eae395 100644 (file)
 # Offers the base 64 encoding and decoding algorithms
 module base64
 
-redef class String
-
+redef class NativeString
        # Alphabet used by the base64 algorithm
-       private fun base64_chars : String
+       private fun base64_chars : SequenceRead[Byte]
        do
-               return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+               return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".bytes
        end
+
+       # Reversed alphabet for base64
        private fun inverted_base64_chars : HashMap[Byte, Byte]
        do
                var inv_base64_chars = new HashMap[Byte, Byte]
-               for k in [0..base64_chars.bytelen[ do
-                       inv_base64_chars[base64_chars.bytes[k]] = k.to_b
+               var l = base64_chars.length
+               for k in [0 .. l[ do
+                       inv_base64_chars[base64_chars[k]] = k.to_b
                end
                return inv_base64_chars
        end
 
-       # Encodes the receiver string to base64.
+       # Encodes `self` to base64.
+       #
        # By default, uses "=" for padding.
-       fun encode_base64 : String do return encode_base64_custom_padding('='.ascii.to_b)
-
-       # Encodes the receiver string to base64 using a custom padding character.
        #
-       # If using the default padding character `=`, see `encode_base64`.
-       fun encode_base64_custom_padding(padding : Byte) : String
-       do
-               var base64_bytes = once base64_chars.bytes
-               var length = bytelen
-
+       #     assert "string".encode_base64 == "c3RyaW5n"
+       private fun encode_base64(length: Int, padding: nullable Byte): Bytes do
+               var base64_bytes = once base64_chars
+               if padding == null then padding = '='.ascii.to_b
                var steps = length / 3
                var bytes_in_last_step = length % 3
                var result_length = steps * 4
                if bytes_in_last_step > 0 then result_length += 4
-               var result = new NativeString(result_length + 1)
-               var bytes = self.bytes
-               result[result_length] = 0u8
-
-               var mask_6bit = 0b0011_1111
+               var result = new Bytes.with_capacity(result_length)
 
+               var in_off = 0
                for s in [0 .. steps[ do
-                       var e = 0
-                       for ss in [0 .. 3[ do
-                               e += bytes[s * 3 + ss].to_i << ((2 - ss) * 8)
-                       end
-                       for ss in [0..4[ do
-                               result[s * 4 + 3 - ss] = base64_bytes[(e >> (ss * 6)) & mask_6bit]
-                       end
+                       var ind = ((self[in_off] & 0b1111_1100u8) >> 2).to_i
+                       result.add base64_bytes[ind]
+                       ind = ((self[in_off] & 0b0000_0011u8) << 4).to_i | ((self[in_off + 1] & 0b1111_0000u8) >> 4).to_i
+                       result.add base64_bytes[ind]
+                       ind = ((self[in_off + 1] & 0b0000_1111u8) << 2).to_i | ((self[in_off + 2] & 0b1100_0000u8) >> 6).to_i
+                       result.add base64_bytes[ind]
+                       ind = (self[in_off + 2] & 0b0011_1111u8).to_i
+                       result.add base64_bytes[ind]
+                       in_off += 3
                end
-
-               var out_off = result_length - 4
-               var in_off = length - bytes_in_last_step
                if bytes_in_last_step == 1 then
-                       result[out_off] = base64_bytes[((bytes[in_off] & 0b1111_1100u8) >> 2).to_i]
-                       result[out_off + 1] = base64_bytes[((bytes[in_off] & 0b0000_0011u8) << 4).to_i]
-                       out_off += 2
+                       result.add base64_bytes[((self[in_off] & 0b1111_1100u8) >> 2).to_i]
+                       result.add base64_bytes[((self[in_off] & 0b0000_0011u8) << 4).to_i]
                else if bytes_in_last_step == 2 then
-                       result[out_off] = base64_bytes[((bytes[in_off] & 0b1111_1100u8) >> 2).to_i]
-                       result[out_off + 1] = base64_bytes[(((bytes[in_off] & 0b0000_0011u8) << 4) | ((bytes[in_off + 1] & 0b1111_0000u8) >> 4)).to_i]
-                       result[out_off + 2] = base64_bytes[((bytes[in_off + 1] & 0b0000_1111u8) << 2).to_i]
-                       out_off += 3
-               end
-               if bytes_in_last_step > 0 then
-                       for i in [out_off .. result_length[ do result[i] = padding
+                       result.add base64_bytes[((self[in_off] & 0b1111_1100u8) >> 2).to_i]
+                       result.add base64_bytes[(((self[in_off] & 0b0000_0011u8) << 4) | ((self[in_off + 1] & 0b1111_0000u8) >> 4)).to_i]
+                       result.add base64_bytes[((self[in_off + 1] & 0b0000_1111u8) << 2).to_i]
                end
+               var rempad = if bytes_in_last_step > 0 then 3 - bytes_in_last_step else 0
+               for i in [0 .. rempad[ do result.add padding
 
-               return result.to_s_with_length(result_length)
+               return result
        end
 
-       # Decodes the receiver string from base64.
-       # By default, uses "=" for padding.
-       fun decode_base64 : String do return decode_base64_custom_padding('='.ascii.to_b)
-
-       # Decodes the receiver string to base64 using a custom padding character.
+       # Decodes `self` from base64
        #
-       # If using the default padding character `=`, see `decode_base64`.
-       fun decode_base64_custom_padding(padding : Byte) : String
-       do
+       #      assert "c3RyaW5n".decode_base64 == "string"
+       #
+       # REQUIRE: `length % 4 == 0`
+       private fun decode_base64(length: Int, padding: nullable Byte): Bytes do
+               if padding == null then padding = '='.ascii.to_b
                var inv = once inverted_base64_chars
-               var length = bytelen
-               if length == 0 then return ""
+               if length == 0 then return new Bytes.empty
                assert length % 4 == 0 else print "base64::decode_base64 only supports strings of length multiple of 4"
 
-               var bytes = self.bytes
+               var bytes = self
                var steps = length / 4
                var result_length = steps * 3
 
@@ -113,17 +101,16 @@ redef class String
                if padding_len == 1 then result_length -= 1
                if padding_len == 2 then result_length -= 2
 
-               var result = new NativeString(result_length + 1)
-               result[result_length] = 0u8
+               var result = new Bytes.with_capacity(result_length + 1)
 
                for s in [0 .. steps[ do
                        var c0 = inv[bytes[s * 4]]
                        var c1 = inv[bytes[s * 4 + 1]]
                        var c2 = inv[bytes[s * 4 + 2]]
                        var c3 = inv[bytes[s * 4 + 3]]
-                       result[s * 3] = ((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)
-                       result[s * 3 + 1] = ((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2)
-                       result[s * 3 + 2] = ((c2 & 0b0000_0011u8) << 6) | (c3 & 0b0011_1111u8)
+                       result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4))
+                       result.add (((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2))
+                       result.add (((c2 & 0b0000_0011u8) << 6) | (c3 & 0b0011_1111u8))
                end
 
                var last_start = steps * 4
@@ -131,14 +118,52 @@ redef class String
                        var c0 = inv[bytes[last_start]]
                        var c1 = inv[bytes[last_start + 1]]
                        var c2 = inv[bytes[last_start + 2]]
-                       result[result_length - 2] = ((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)
-                       result[result_length - 1] = ((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2)
+                       result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4))
+                       result.add (((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2))
                else if padding_len == 2 then
                        var c0 = inv[bytes[last_start]]
                        var c1 = inv[bytes[last_start + 1]]
-                       result[result_length - 1] = ((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)
+                       result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4))
                end
 
-               return result.to_s_with_length(result_length)
+               return result
+       end
+end
+
+redef class Bytes
+
+       # Encodes the receiver string to base64 using a custom padding character.
+       #
+       # If using the default padding character `=`, see `encode_base64`.
+       fun encode_base64(padding: nullable Byte): Bytes
+       do
+               return items.encode_base64(length, padding)
+       end
+
+       # Decodes the receiver string to base64 using a custom padding character.
+       #
+       # Default padding character `=`
+       fun decode_base64(padding : nullable Byte) : Bytes
+       do
+               return items.decode_base64(length, padding)
+       end
+end
+
+redef class String
+
+       # Encodes the receiver string to base64 using a custom padding character.
+       #
+       # If using the default padding character `=`, see `encode_base64`.
+       fun encode_base64(padding: nullable Byte): String
+       do
+               return to_cstring.encode_base64(bytelen, padding).to_s
+       end
+
+       # Decodes the receiver string to base64 using a custom padding character.
+       #
+       # Default padding character `=`
+       fun decode_base64(padding : nullable Byte) : String
+       do
+               return to_cstring.decode_base64(bytelen, padding).to_s
        end
 end
index 332efc2..d589646 100644 (file)
@@ -19,6 +19,16 @@ import kernel
 import collection::array
 intrude import text::flat
 
+redef class Byte
+       # Write self as a string into `ns` at position `pos`
+       private fun add_digest_at(ns: NativeString, pos: Int) do
+               var tmp = (0xF0u8 & self) >> 4
+               ns[pos] = if tmp >= 0x0Au8 then tmp + 0x37u8 else tmp + 0x30u8
+               tmp = 0x0Fu8 & self
+               ns[pos + 1] = if tmp >= 0x0Au8 then tmp + 0x37u8 else tmp + 0x30u8
+       end
+end
+
 # A buffer containing Byte-manipulation facilities
 #
 # Uses Copy-On-Write when persisted
@@ -26,7 +36,7 @@ class Bytes
        super AbstractArray[Byte]
 
        # A NativeString being a char*, it can be used as underlying representation here.
-       private var items: NativeString
+       var items: NativeString
 
        # Number of bytes in the array
        redef var length
@@ -63,6 +73,20 @@ class Bytes
                return items[i]
        end
 
+       # Returns self as a hexadecimal digest
+       fun hexdigest: String do
+               var elen = length * 2
+               var ns = new NativeString(elen)
+               var i = 0
+               var oi = 0
+               while i < length do
+                       self[i].add_digest_at(ns, oi)
+                       i += 1
+                       oi += 2
+               end
+               return new FlatString.full(ns, elen, 0, elen - 1, elen)
+       end
+
        #     var b = new Bytes.with_capacity(1)
        #     b[0] = 101u8
        #     assert b.to_s == "e"
index 8a5acc9..2bd8fbe 100644 (file)
@@ -1,7 +1,5 @@
 # This file is part of NIT (http://www.nitlanguage.org).
 #
-# Copyright 2014 Lucas Bajolet <r4pass@hotmail.com>
-#
 # 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
@@ -219,22 +217,12 @@ in "C Header" `{
        }
 `}
 
-redef class String
-
-       # Computes the SHA1 of the receiver
-       #
-       # Returns a digest of 20 bytes as a String,
-       # note that all the characters are not necessarily ASCII.
-       # If you want the hex string version of the digest, use
-       # sha1_to_s.
-       #
-       #     import base64
-       #     assert "The quick brown fox jumps over the lazy dog".sha1.encode_base64 == "L9ThxnotKPzthJ7hu3bnORuT6xI="
-       fun sha1: String import String.to_cstring, String.length, NativeString.to_s_with_length `{
+redef class NativeString
+       private fun sha1_intern(len: Int): NativeString `{
                sha1nfo s;
 
                sha1_init(&s);
-               sha1_write(&s, String_to_cstring(self), String_length(self));
+               sha1_write(&s, self, len);
                uint8_t* digest = sha1_result(&s);
 
                char* digested = malloc(21);
@@ -243,35 +231,30 @@ redef class String
 
                digested[20] = '\0';
 
-               return NativeString_to_s_with_length(digested, 20);
+               return digested;
        `}
+end
+
+redef class String
+
+       # Computes the SHA1 of the receiver
+       #
+       # Returns a digest of 20 bytes as a NativeString,
+       # note that all the characters are not necessarily ASCII.
+       # If you want the hex string version of the digest, use
+       # sha1_hexdigest.
+       #
+       #     import base64
+       #     assert "The quick brown fox jumps over the lazy dog".sha1 == [0x2Fu8, 0xD4u8, 0xE1u8, 0xC6u8, 0x7Au8, 0x2Du8, 0x28u8, 0xFCu8, 0xEDu8, 0x84u8, 0x9Eu8, 0xE1u8, 0xBBu8, 0x76u8, 0xE7u8, 0x39u8, 0x1Bu8, 0x93u8, 0xEBu8, 0x12u8]
+       fun sha1: Bytes do
+               return new Bytes(to_cstring.sha1_intern(bytelen), 20, 20)
+       end
 
        # Computes the SHA1 of the receiver.
        #
        # Returns a 40 char String containing the Hexadecimal
        # Digest in its Char form.
        #
-       #     assert "The quick brown fox jumps over the lazy dog".sha1_to_s == "2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12"
-       fun sha1_to_s: String import String.to_cstring, String.length, NativeString.to_s_with_length `{
-               sha1nfo s;
-
-               sha1_init(&s);
-               sha1_write(&s, String_to_cstring(self), String_length(self));
-               uint8_t* digest = sha1_result(&s);
-
-               char* ret_str = malloc(41);
-               char* hexmap = "0123456789ABCDEF";
-
-               int i;
-               for(i=0;i<20;i++){
-                       uint8_t q = digest[i];
-                       ret_str[i*2] = hexmap[q >> 4];
-                       ret_str[(i*2)+1] = hexmap[q & 0x0F];
-               }
-               ret_str[40] = '\0';
-
-               return NativeString_to_s_with_length(ret_str, 40);
-       `}
-
+       #     assert "The quick brown fox jumps over the lazy dog".sha1_hexdigest == "2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12"
+       fun sha1_hexdigest: String do return sha1.hexdigest
 end
-
index c3fdaad..2a3c32f 100644 (file)
@@ -114,7 +114,7 @@ class WebsocketConnection
                resp_map["Connection:"] = "Upgrade"
                var key = heads["Sec-WebSocket-Key"]
                key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-               key = key.sha1.encode_base64
+               key = key.sha1.encode_base64.to_s
                resp_map["Sec-WebSocket-Accept:"] = key
                var resp = resp_map.join("\r\n", " ")
                resp += "\r\n\r\n"