├── .gitignore ├── LICENSE ├── README.md ├── benchmark.mojo ├── fast_base64 ├── __init__.mojo ├── chromium.mojo └── simd.mojo ├── python_benchmark.py ├── samples └── __init__.mojo └── test.mojo /.gitignore: -------------------------------------------------------------------------------- 1 | /main.mojo -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2024, Maxim Zaks 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast Base64 written in Mojo 2 | 3 | This project is an adoptation of https://github.com/lemire/fastbase64 project. 4 | 5 | Currently we adopted only the chromium base64 alogirth, which is already about 5x faster than the Mojo standard library b64encode function. 6 | 7 | In the future we aim at adopting the SIMD based algorithm and hope for further speedups. -------------------------------------------------------------------------------- /benchmark.mojo: -------------------------------------------------------------------------------- 1 | from base64 import b64encode, b64decode 2 | from fast_base64 import encode, decode 3 | from time import now 4 | from testing import assert_equal 5 | from samples import mobi_dick_plain 6 | from pathlib import Path 7 | 8 | 9 | fn main() raises: 10 | var text = Path("/usr/share/dict/words").read_text() 11 | # var text = String(mobi_dick_plain) 12 | var std_min_d_enc = 10000000 13 | var h0 = String("") 14 | for _ in range(10): 15 | var tik = now() 16 | h0 = b64encode(text) 17 | var tok = now() 18 | if std_min_d_enc > tok - tik: 19 | std_min_d_enc = tok - tik 20 | print("Std b64 encode:", (std_min_d_enc) / len(text), len(text)) 21 | 22 | var h1 = String("") 23 | var fast_min_d_enc = 10000000 24 | for _ in range(10): 25 | var tik = now() 26 | h1 = encode(text) 27 | var tok = now() 28 | if fast_min_d_enc > tok - tik: 29 | fast_min_d_enc = tok - tik 30 | 31 | print("Fast b64 encode:", (fast_min_d_enc) / len(text), len(h1), len(text)) 32 | print("Encoding speedup:", Float64(std_min_d_enc) / (fast_min_d_enc)) 33 | 34 | for i in range(len(h0)): 35 | if h0[i] != h1[i]: 36 | print("std Error:", h0[i], "chrome:", h1[i], "on", i) 37 | break 38 | 39 | var std_min_d_dec = 1000000 40 | for _ in range(10): 41 | var p: DTypePointer[DType.uint8] 42 | var l: Int 43 | var tik = now() 44 | var s = b64decode(h0) 45 | var tok = now() 46 | assert_equal(text, s) 47 | if std_min_d_dec > tok - tik: 48 | std_min_d_dec = tok - tik 49 | 50 | print("Std Decode:", std_min_d_dec / len(h1)) 51 | 52 | var fast_min_d_dec = 1000000 53 | for _ in range(10): 54 | var p: DTypePointer[DType.uint8] 55 | var l: Int 56 | var tik = now() 57 | p, l = decode[zero_terminated=True](h1) 58 | var tok = now() 59 | assert_equal(text, String(p.bitcast[DType.int8](), l)) 60 | if fast_min_d_dec > tok - tik: 61 | fast_min_d_dec = tok - tik 62 | 63 | print("Fast Decode:", fast_min_d_dec / len(h1)) 64 | print("Decoding speedup:", Float64(std_min_d_dec) / (fast_min_d_dec)) 65 | _ = text 66 | -------------------------------------------------------------------------------- /fast_base64/__init__.mojo: -------------------------------------------------------------------------------- 1 | from .chromium import decode 2 | from .simd import encode -------------------------------------------------------------------------------- /fast_base64/chromium.mojo: -------------------------------------------------------------------------------- 1 | from tensor import Tensor 2 | 3 | alias cpad = "=".data().bitcast[DType.uint8]()[0] 4 | alias e0 = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ" 5 | "aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmmnnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzz" 6 | "0000111122223333444455556666777788889999++++////".data().bitcast[DType.uint8]() 7 | alias e1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 8 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 9 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".data().bitcast[DType.uint8]() 11 | 12 | alias d0 = compute_d0() 13 | 14 | fn compute_d0() -> DTypePointer[DType.uint32]: 15 | var d = SIMD[DType.uint32, 256]( 16 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 17 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 18 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 19 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 20 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 21 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 22 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 23 | 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, 24 | 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, 25 | 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, 26 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 27 | 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, 28 | 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, 29 | 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, 30 | 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, 31 | 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 32 | 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, 33 | 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, 34 | 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, 35 | 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, 36 | 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, 37 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 38 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 39 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 40 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 41 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 42 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 43 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 44 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 45 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 46 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 47 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 48 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 49 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 50 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 51 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 52 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 53 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 54 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 55 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 56 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 57 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 58 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 59 | ) 60 | var p = DTypePointer[DType.uint32].alloc(256) 61 | p.store(d) 62 | return p 63 | 64 | 65 | alias d1 = compute_d1() 66 | 67 | fn compute_d1() -> DTypePointer[DType.uint32]: 68 | var d = SIMD[DType.uint32, 256]( 69 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 70 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 71 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 72 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 73 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 74 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 75 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 76 | 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, 77 | 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, 78 | 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, 79 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 80 | 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, 81 | 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, 82 | 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, 83 | 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, 84 | 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 85 | 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, 86 | 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, 87 | 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, 88 | 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, 89 | 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 90 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 91 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 92 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 93 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 94 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 95 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 96 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 97 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 98 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 99 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 100 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 101 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 102 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 103 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 104 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 105 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 106 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 107 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 108 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 109 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 110 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 111 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 112 | ) 113 | var p = DTypePointer[DType.uint32].alloc(256) 114 | p.store(d) 115 | return p 116 | 117 | alias d2 = compute_d2() 118 | 119 | fn compute_d2() -> DTypePointer[DType.uint32]: 120 | var d = SIMD[DType.uint32, 256]( 121 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 122 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 123 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 124 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 125 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 126 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 127 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 128 | 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, 129 | 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, 130 | 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, 131 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 132 | 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, 133 | 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, 134 | 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, 135 | 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, 136 | 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 137 | 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, 138 | 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, 139 | 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, 140 | 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, 141 | 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 142 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 143 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 144 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 145 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 146 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 147 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 148 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 149 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 150 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 151 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 152 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 153 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 154 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 155 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 156 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 157 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 158 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 159 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 160 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 161 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 162 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 163 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 164 | ) 165 | var p = DTypePointer[DType.uint32].alloc(256) 166 | p.store(d) 167 | return p 168 | 169 | alias d3 = compute_d3() 170 | 171 | fn compute_d3() -> DTypePointer[DType.uint32]: 172 | var d = SIMD[DType.uint32, 256]( 173 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 174 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 175 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 176 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 177 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 178 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 179 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 180 | 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, 181 | 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, 182 | 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, 183 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, 184 | 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, 185 | 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, 186 | 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, 187 | 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, 188 | 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 189 | 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, 190 | 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, 191 | 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, 192 | 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, 193 | 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 194 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 195 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 196 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 197 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 198 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 199 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 200 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 201 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 202 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 203 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 204 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 205 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 206 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 207 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 208 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 209 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 210 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 211 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 212 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 213 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 214 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 215 | 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff 216 | ) 217 | var p = DTypePointer[DType.uint32].alloc(256) 218 | p.store(d) 219 | return p 220 | 221 | alias BADCHAR = UInt32(0x01FFFFFF) 222 | 223 | @always_inline 224 | fn encode(input: StringLiteral) -> String: 225 | return encode(input.data().bitcast[DType.uint8](), len(input)) 226 | 227 | @always_inline 228 | fn encode(input: String) -> String: 229 | return encode(input._as_ptr().bitcast[DType.uint8](), len(input)) 230 | 231 | @always_inline 232 | fn encode(input: Tensor) -> String: 233 | return encode(input.data().bitcast[DType.uint8](), input.bytecount()) 234 | 235 | @always_inline 236 | fn encode(input: DTypePointer[DType.uint8], length: Int) -> String: 237 | var result_size = (length + 2) // 3 * 4 + 1 238 | var result = DTypePointer[DType.int8].alloc(result_size) 239 | 240 | _encode(input, length, result.bitcast[DType.uint8]()) 241 | 242 | result.store(result_size - 1, 0) 243 | return String(result, result_size) 244 | 245 | @always_inline 246 | fn _encode(input: DTypePointer[DType.uint8], length: Int, output: DTypePointer[DType.uint8]): 247 | var processed = 0 248 | var p = output 249 | if length > 2: 250 | for i in range(0, length - 2, 3): 251 | var t1 = int(input.load(i)) 252 | var t2 = int(input.load(i+1)) 253 | var t3 = int(input.load(i+2)) 254 | var bytes = SIMD[DType.uint8, 4]( 255 | e0.load(t1), 256 | e1.load(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)), 257 | e1.load(((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)), 258 | e1.load(t3) 259 | ) 260 | p.simd_nt_store(bytes) 261 | p = p.offset(4) 262 | processed = i + 3 263 | 264 | var rest = length - processed 265 | if rest == 1: 266 | var t1 = int(input.load(processed)) 267 | var bytes = SIMD[DType.uint8, 4]( 268 | e0.load(t1), 269 | e1.load(((t1 & 0x03) << 4)), 270 | cpad, 271 | cpad 272 | ) 273 | p.simd_nt_store(bytes) 274 | elif rest == 2: 275 | var t1 = int(input.load(processed)) 276 | var t2 = int(input.load(processed+1)) 277 | var bytes = SIMD[DType.uint8, 4]( 278 | e0.load(t1), 279 | e1.load(((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)), 280 | e1.load(((t2 & 0x0F) << 2)), 281 | cpad 282 | ) 283 | p.simd_nt_store(bytes) 284 | 285 | @always_inline 286 | fn decode[zero_terminated: Bool = False](input: String) raises -> (DTypePointer[DType.uint8], Int): 287 | var input_size = len(input) 288 | var input_pointer = input._as_ptr().bitcast[DType.uint8]() 289 | if input_size == 0: 290 | return DTypePointer[DType.uint8](), 0 291 | 292 | if input_size & 3 != 0: 293 | raise "We do not support unpadded base64 strings" 294 | 295 | # input size without padding 296 | input_size -= int(input_pointer.load(input_size - 2) == cpad) + int(input_pointer.load(input_size - 1) == cpad) 297 | 298 | var result_size = (input_size >> 2) * 3 299 | @parameter 300 | if zero_terminated: 301 | result_size += 1 302 | var result = DTypePointer[DType.uint8].alloc(result_size) 303 | 304 | var leftover = input_size & 3 305 | var chunks = input_size >> 2 306 | 307 | var bad_char = False 308 | var destination = result 309 | for i in range(chunks): 310 | var x = d0[input_pointer[0]] | d1[input_pointer[1]] | d2[input_pointer[2]] | d3[input_pointer[3]] 311 | bad_char = bad_char or (x >= BADCHAR) 312 | var xu8 = bitcast[DType.uint8, 4](x) 313 | destination.store(xu8[0]) 314 | destination.store(1, xu8[1]) 315 | destination.store(2, xu8[2]) 316 | destination = destination.offset(3) 317 | input_pointer = input_pointer.offset(4) 318 | 319 | if leftover == 2: 320 | var x = d0[input_pointer[0]] | d1[input_pointer[1]] 321 | bad_char = bad_char or (x >= BADCHAR) 322 | var xu8 = bitcast[DType.uint8, 4](x) 323 | destination.store(xu8[0]) 324 | result_size += 1 325 | elif leftover == 3: 326 | var x = d0[input_pointer[0]] | d1[input_pointer[1]] | d2[input_pointer[2]] 327 | bad_char = bad_char or (x >= BADCHAR) 328 | var xu8 = bitcast[DType.uint8, 4](x) 329 | destination.store(xu8[0]) 330 | destination.store(1, xu8[1]) 331 | result_size += 2 332 | 333 | if bad_char: 334 | raise "Could not decode. Bad char was identified" 335 | 336 | @parameter 337 | if zero_terminated: 338 | result[result_size - 1] = 0 339 | return result, result_size 340 | -------------------------------------------------------------------------------- /fast_base64/simd.mojo: -------------------------------------------------------------------------------- 1 | from .chromium import _encode 2 | from math import rotate_bits_left 3 | from tensor import Tensor 4 | 5 | @always_inline 6 | fn encode(input: StringLiteral) -> String: 7 | return encode(input.data().bitcast[DType.uint8](), len(input)) 8 | 9 | @always_inline 10 | fn encode(input: String) -> String: 11 | return encode(input._as_ptr().bitcast[DType.uint8](), len(input)) 12 | 13 | @always_inline 14 | fn encode(input: Tensor) -> String: 15 | return encode(input.data().bitcast[DType.uint8](), input.bytecount()) 16 | 17 | @always_inline 18 | fn encode(input: DTypePointer[DType.uint8], length: Int) -> String: 19 | var data = input 20 | var result_size = (length + 2) // 3 * 4 + 1 21 | var result = DTypePointer[DType.int8].alloc(result_size) 22 | var offset = 0 23 | var cursor = result.bitcast[DType.uint8]() 24 | alias simd_width = 32 25 | while length - offset >= simd_width: 26 | var a = data.load[width=simd_width](offset) 27 | # aaaaaabb bbbbcccc ccdddddd ________ 28 | var b = a.shuffle[ 29 | 1, 0, 2, 1, 30 | 4, 3, 5, 4, 31 | 7, 6, 8, 7, 32 | 10, 9, 11, 10, 33 | 13, 12, 14, 13, 34 | 16, 15, 17, 16, 35 | 19, 18, 20, 19, 36 | 22, 21, 23, 22, 37 | ]() 38 | # bbbbcccc aaaaaabb ccdddddd bbbbcccc 39 | 40 | offset += (simd_width >> 2) * 3 41 | 42 | var c = bitcast[DType.uint16, simd_width >> 1](b) 43 | 44 | var d = c.deinterleave() 45 | # d[0] = bbbbcccc aaaaaabb 46 | # d[1] = ccdddddd bbbbcccc 47 | 48 | # TODO: this implementaiton is for little endian only add big endian support 49 | 50 | var d1 = rotate_bits_left[6](d[0]).cast[DType.uint8]() 51 | # d1 = ccaaaaaa 52 | var d2 = rotate_bits_left[12](d[0]).cast[DType.uint8]() 53 | # d2 = aabbbbbb 54 | var d3 = rotate_bits_left[10](d[1]).cast[DType.uint8]() 55 | # d3 = bbcccccc 56 | var d4 = d[1].cast[DType.uint8]() 57 | # d4 = ccdddddd 58 | 59 | var e1 = d1.interleave(d3) 60 | # e1 = ccaaaaaa bbcccccc 61 | var e2 = d2.interleave(d4) 62 | # e2 = aabbbbbb ccdddddd 63 | var e3 = e1.interleave(e2) & 0b0011_1111 64 | # e3 = 00aaaaaa 00bbbbbb 00cccccc 00dddddd 65 | 66 | var upper = e3 < 26 67 | var lower = (e3 > 25) & (e3 < 52) 68 | var nums = (e3 > 51) & (e3 < 62) 69 | var plus = e3 == 62 70 | var slash = e3 == 63 71 | 72 | var f1 = upper.select(e3 + 65, 0) 73 | var f2 = lower.select(e3 + 71, 0) 74 | var f3 = nums.select(e3 - 4, 0) 75 | var f4 = plus.select(e3 - 19, 0) 76 | var f5 = slash.select(e3 - 16, 0) 77 | 78 | # 0 .. 25 -> 65 .. 90 (+65) A .. Z 79 | # 26 .. 51 -> 97 .. 122 (+71) a .. z 80 | # 52 .. 61 -> 48 .. 57 (-4) 0 .. 9 81 | # 62 -> 43 (-19) + 82 | # 63 -> 47 (-16) / 83 | 84 | cursor.simd_nt_store(0, f1 + f2 + f3 + f4 + f5) 85 | 86 | cursor = cursor.offset(simd_width) 87 | 88 | if length > offset: 89 | _encode(data.offset(offset), length - offset, cursor) 90 | 91 | result.store(result_size - 1, 0) 92 | return String(result, result_size) 93 | -------------------------------------------------------------------------------- /python_benchmark.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import time 3 | 4 | mobi_dick_plain = ( 5 | "Call me Ishmael. Some years ago--never mind how long precisely--having\n" 6 | "little or no money in my purse, and nothing particular to interest me on\n" 7 | "shore, I thought I would sail about a little and see the watery part of\n" 8 | "the world. It is a way I have of driving off the spleen and regulating\n" 9 | "the circulation. Whenever I find myself growing grim about the mouth;\n" 10 | "whenever it is a damp, drizzly November in my soul; whenever I find\n" 11 | "myself involuntarily pausing before coffin warehouses, and bringing up\n" 12 | "the rear of every funeral I meet; and especially whenever my hypos get\n" 13 | "such an upper hand of me, that it requires a strong moral principle to\n" 14 | "prevent me from deliberately stepping into the street, and methodically\n" 15 | "knocking people's hats off--then, I account it high time to get to sea\n" 16 | "as soon as I can. This is my substitute for pistol and ball. With a\n" 17 | "philosophical flourish Cato throws himself upon his sword; I quietly\n" 18 | "take to the ship. There is nothing surprising in this. If they but knew\n" 19 | "it, almost all men in their degree, some time or other, cherish very\n" 20 | "nearly the same feelings towards the ocean with me.\n" 21 | ) 22 | 23 | mobi_dick_base64 = ( 24 | "Q2FsbCBtZSBJc2htYWVsLiBTb21lIHllYXJzIGFnby0tbmV2ZXIgbWluZCBob3cgbG9uZ" 25 | "yBwcmVjaXNlbHktLWhhdmluZwpsaXR0bGUgb3Igbm8gbW9uZXkgaW4gbXkgcHVyc2UsIG" 26 | "FuZCBub3RoaW5nIHBhcnRpY3VsYXIgdG8gaW50ZXJlc3QgbWUgb24Kc2hvcmUsIEkgdGh" 27 | "vdWdodCBJIHdvdWxkIHNhaWwgYWJvdXQgYSBsaXR0bGUgYW5kIHNlZSB0aGUgd2F0ZXJ5" 28 | "IHBhcnQgb2YKdGhlIHdvcmxkLiBJdCBpcyBhIHdheSBJIGhhdmUgb2YgZHJpdmluZyBvZ" 29 | "mYgdGhlIHNwbGVlbiBhbmQgcmVndWxhdGluZwp0aGUgY2lyY3VsYXRpb24uIFdoZW5ldm" 30 | "VyIEkgZmluZCBteXNlbGYgZ3Jvd2luZyBncmltIGFib3V0IHRoZSBtb3V0aDsKd2hlbmV" 31 | "2ZXIgaXQgaXMgYSBkYW1wLCBkcml6emx5IE5vdmVtYmVyIGluIG15IHNvdWw7IHdoZW5l" 32 | "dmVyIEkgZmluZApteXNlbGYgaW52b2x1bnRhcmlseSBwYXVzaW5nIGJlZm9yZSBjb2Zma" 33 | "W4gd2FyZWhvdXNlcywgYW5kIGJyaW5naW5nIHVwCnRoZSByZWFyIG9mIGV2ZXJ5IGZ1bm" 34 | "VyYWwgSSBtZWV0OyBhbmQgZXNwZWNpYWxseSB3aGVuZXZlciBteSBoeXBvcyBnZXQKc3V" 35 | "jaCBhbiB1cHBlciBoYW5kIG9mIG1lLCB0aGF0IGl0IHJlcXVpcmVzIGEgc3Ryb25nIG1v" 36 | "cmFsIHByaW5jaXBsZSB0bwpwcmV2ZW50IG1lIGZyb20gZGVsaWJlcmF0ZWx5IHN0ZXBwa" 37 | "W5nIGludG8gdGhlIHN0cmVldCwgYW5kIG1ldGhvZGljYWxseQprbm9ja2luZyBwZW9wbG" 38 | "UncyBoYXRzIG9mZi0tdGhlbiwgSSBhY2NvdW50IGl0IGhpZ2ggdGltZSB0byBnZXQgdG8" 39 | "gc2VhCmFzIHNvb24gYXMgSSBjYW4uIFRoaXMgaXMgbXkgc3Vic3RpdHV0ZSBmb3IgcGlz" 40 | "dG9sIGFuZCBiYWxsLiBXaXRoIGEKcGhpbG9zb3BoaWNhbCBmbG91cmlzaCBDYXRvIHRoc" 41 | "m93cyBoaW1zZWxmIHVwb24gaGlzIHN3b3JkOyBJIHF1aWV0bHkKdGFrZSB0byB0aGUgc2" 42 | "hpcC4gVGhlcmUgaXMgbm90aGluZyBzdXJwcmlzaW5nIGluIHRoaXMuIElmIHRoZXkgYnV" 43 | "0IGtuZXcKaXQsIGFsbW9zdCBhbGwgbWVuIGluIHRoZWlyIGRlZ3JlZSwgc29tZSB0aW1l" 44 | "IG9yIG90aGVyLCBjaGVyaXNoIHZlcnkKbmVhcmx5IHRoZSBzYW1lIGZlZWxpbmdzIHRvd" 45 | "2FyZHMgdGhlIG9jZWFuIHdpdGggbWUuCg==" 46 | ) 47 | 48 | if __name__ == "__main__": 49 | 50 | min_duration = 10000000 51 | for _ in range(10): 52 | utf8_string = mobi_dick_plain.encode('utf-8') 53 | tik = time.time_ns() 54 | plain = base64.b64encode(utf8_string) 55 | tok = time.time_ns() 56 | if min_duration > tok - tik: 57 | min_duration = tok - tik 58 | print("Encode:", min_duration / len(mobi_dick_plain)) 59 | 60 | min_duration = 10000000 61 | for _ in range(10): 62 | tik = time.time_ns() 63 | plain = base64.b64decode(mobi_dick_base64) 64 | tok = time.time_ns() 65 | if min_duration > tok - tik: 66 | min_duration = tok - tik 67 | print("Decode:", min_duration / len(mobi_dick_base64)) -------------------------------------------------------------------------------- /samples/__init__.mojo: -------------------------------------------------------------------------------- 1 | alias mobi_dick_plain = "Call me Ishmael. Some years ago--never mind how long precisely--having\n" 2 | "little or no money in my purse, and nothing particular to interest me on\n" 3 | "shore, I thought I would sail about a little and see the watery part of\n" 4 | "the world. It is a way I have of driving off the spleen and regulating\n" 5 | "the circulation. Whenever I find myself growing grim about the mouth;\n" 6 | "whenever it is a damp, drizzly November in my soul; whenever I find\n" 7 | "myself involuntarily pausing before coffin warehouses, and bringing up\n" 8 | "the rear of every funeral I meet; and especially whenever my hypos get\n" 9 | "such an upper hand of me, that it requires a strong moral principle to\n" 10 | "prevent me from deliberately stepping into the street, and methodically\n" 11 | "knocking people's hats off--then, I account it high time to get to sea\n" 12 | "as soon as I can. This is my substitute for pistol and ball. With a\n" 13 | "philosophical flourish Cato throws himself upon his sword; I quietly\n" 14 | "take to the ship. There is nothing surprising in this. If they but knew\n" 15 | "it, almost all men in their degree, some time or other, cherish very\n" 16 | "nearly the same feelings towards the ocean with me.\n" 17 | alias mobi_dick_base64 = "Q2FsbCBtZSBJc2htYWVsLiBTb21lIHllYXJzIGFnby0tbmV2ZXIgbWluZCBob3cgbG9uZ" 18 | "yBwcmVjaXNlbHktLWhhdmluZwpsaXR0bGUgb3Igbm8gbW9uZXkgaW4gbXkgcHVyc2UsIG" 19 | "FuZCBub3RoaW5nIHBhcnRpY3VsYXIgdG8gaW50ZXJlc3QgbWUgb24Kc2hvcmUsIEkgdGh" 20 | "vdWdodCBJIHdvdWxkIHNhaWwgYWJvdXQgYSBsaXR0bGUgYW5kIHNlZSB0aGUgd2F0ZXJ5" 21 | "IHBhcnQgb2YKdGhlIHdvcmxkLiBJdCBpcyBhIHdheSBJIGhhdmUgb2YgZHJpdmluZyBvZ" 22 | "mYgdGhlIHNwbGVlbiBhbmQgcmVndWxhdGluZwp0aGUgY2lyY3VsYXRpb24uIFdoZW5ldm" 23 | "VyIEkgZmluZCBteXNlbGYgZ3Jvd2luZyBncmltIGFib3V0IHRoZSBtb3V0aDsKd2hlbmV" 24 | "2ZXIgaXQgaXMgYSBkYW1wLCBkcml6emx5IE5vdmVtYmVyIGluIG15IHNvdWw7IHdoZW5l" 25 | "dmVyIEkgZmluZApteXNlbGYgaW52b2x1bnRhcmlseSBwYXVzaW5nIGJlZm9yZSBjb2Zma" 26 | "W4gd2FyZWhvdXNlcywgYW5kIGJyaW5naW5nIHVwCnRoZSByZWFyIG9mIGV2ZXJ5IGZ1bm" 27 | "VyYWwgSSBtZWV0OyBhbmQgZXNwZWNpYWxseSB3aGVuZXZlciBteSBoeXBvcyBnZXQKc3V" 28 | "jaCBhbiB1cHBlciBoYW5kIG9mIG1lLCB0aGF0IGl0IHJlcXVpcmVzIGEgc3Ryb25nIG1v" 29 | "cmFsIHByaW5jaXBsZSB0bwpwcmV2ZW50IG1lIGZyb20gZGVsaWJlcmF0ZWx5IHN0ZXBwa" 30 | "W5nIGludG8gdGhlIHN0cmVldCwgYW5kIG1ldGhvZGljYWxseQprbm9ja2luZyBwZW9wbG" 31 | "UncyBoYXRzIG9mZi0tdGhlbiwgSSBhY2NvdW50IGl0IGhpZ2ggdGltZSB0byBnZXQgdG8" 32 | "gc2VhCmFzIHNvb24gYXMgSSBjYW4uIFRoaXMgaXMgbXkgc3Vic3RpdHV0ZSBmb3IgcGlz" 33 | "dG9sIGFuZCBiYWxsLiBXaXRoIGEKcGhpbG9zb3BoaWNhbCBmbG91cmlzaCBDYXRvIHRoc" 34 | "m93cyBoaW1zZWxmIHVwb24gaGlzIHN3b3JkOyBJIHF1aWV0bHkKdGFrZSB0byB0aGUgc2" 35 | "hpcC4gVGhlcmUgaXMgbm90aGluZyBzdXJwcmlzaW5nIGluIHRoaXMuIElmIHRoZXkgYnV" 36 | "0IGtuZXcKaXQsIGFsbW9zdCBhbGwgbWVuIGluIHRoZWlyIGRlZ3JlZSwgc29tZSB0aW1l" 37 | "IG9yIG90aGVyLCBjaGVyaXNoIHZlcnkKbmVhcmx5IHRoZSBzYW1lIGZlZWxpbmdzIHRvd" 38 | "2FyZHMgdGhlIG9jZWFuIHdpdGggbWUuCg==" 39 | -------------------------------------------------------------------------------- /test.mojo: -------------------------------------------------------------------------------- 1 | from fast_base64 import encode, decode 2 | from fast_base64.chromium import encode as c_encode 3 | from base64 import b64encode 4 | from testing import assert_equal 5 | from samples import mobi_dick_plain, mobi_dick_base64 6 | 7 | fn b64(s: String) raises: 8 | var b = encode(s) 9 | assert_equal(b, b64encode(s)) 10 | assert_equal(b, c_encode(s)) 11 | var p: DTypePointer[DType.uint8] 12 | var length: Int 13 | p, length = decode[zero_terminated=True](b) 14 | if len(s) == 0 and length == 0: 15 | return 16 | var decoded_s = String(p.bitcast[DType.int8](), length) 17 | assert_equal(s, decoded_s) 18 | 19 | fn main() raises: 20 | b64("hello world") 21 | b64("") 22 | b64("h") 23 | b64("ha") 24 | b64("hal") 25 | b64("halo") 26 | b64("AtariAtat") 27 | b64("AtariAtatürkAtatürk's") 28 | 29 | var b_chromium = encode(mobi_dick_plain) 30 | var b_std = b64encode(mobi_dick_plain) 31 | assert_equal(b_chromium, mobi_dick_base64) 32 | assert_equal(b_std, mobi_dick_base64) 33 | 34 | var p: DTypePointer[DType.uint8] 35 | var l: Int 36 | p, l = decode[True](mobi_dick_base64) 37 | assert_equal(mobi_dick_plain, String(p.bitcast[DType.int8](), l)) 38 | --------------------------------------------------------------------------------