Wolfgang Thallergcc 15.1.0ada185d1 year agoHistory
2202 lines · 65.9 KB · D
RawDownload
// Written in the D programming language. /** * Support for Base64 encoding and decoding. * * This module provides two default implementations of Base64 encoding, * $(LREF Base64) with a standard encoding alphabet, and a variant * $(LREF Base64URL) that has a modified encoding alphabet designed to be * safe for embedding in URLs and filenames. * * Both variants are implemented as instantiations of the template * $(LREF Base64Impl). Most users will not need to use this template * directly; however, it can be used to create customized Base64 encodings, * such as one that omits padding characters, or one that is safe to embed * inside a regular expression. * * Example: * ----- * ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]; * * const(char)[] encoded = Base64.encode(data); * assert(encoded == "FPucA9l+"); * * ubyte[] decoded = Base64.decode("FPucA9l+"); * assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]); * ----- * * The range API is supported for both encoding and decoding: * * Example: * ----- * // Create MIME Base64 with CRLF, per line 76. * File f = File("./text.txt", "r"); * scope(exit) f.close(); * * Appender!string mime64 = appender!string; * * foreach (encoded; Base64.encoder(f.byChunk(57))) * { *     mime64.put(encoded); *     mime64.put("\r\n"); * } * * writeln(mime64.data); * ----- * * References: * $(LINK2 https://tools.ietf.org/html/rfc4648, RFC 4648 - The Base16, Base32, and Base64 * Data Encodings) * * Copyright: Masahiro Nakagawa 2010-. * License:   $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). * Authors:   Masahiro Nakagawa, Daniel Murphy (Single value Encoder and Decoder) * Source:    $(PHOBOSSRC std/base64.d) * Macros: *      LREF2=<a href="#$1">`$2`</a> */module std.base64; import std.exception : enforce;import std.range.primitives : empty, front, isInputRange, isOutputRange,    isForwardRange, ElementType, hasLength, popFront, put, save;import std.traits : isArray; // Make sure module header code examples work correctly.pure @safe unittest{    ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e];     const(char)[] encoded = Base64.encode(data);    assert(encoded == "FPucA9l+");     ubyte[] decoded = Base64.decode("FPucA9l+");    assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);} /** * Implementation of standard _Base64 encoding. * * See $(LREF Base64Impl) for a description of available methods. */alias Base64 = Base64Impl!('+', '/'); ///pure @safe unittest{    ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];    assert(Base64.encode(data) == "g9cwegE/");    assert(Base64.decode("g9cwegE/") == data);}  /** * Variation of Base64 encoding that is safe for use in URLs and filenames. * * See $(LREF Base64Impl) for a description of available methods. */alias Base64URL = Base64Impl!('-', '_'); ///pure @safe unittest{    ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];    assert(Base64URL.encode(data) == "g9cwegE_");    assert(Base64URL.decode("g9cwegE_") == data);} /** * Unpadded variation of Base64 encoding that is safe for use in URLs and * filenames, as used in RFCs 4648 and 7515 (JWS/JWT/JWE). * * See $(LREF Base64Impl) for a description of available methods. */alias Base64URLNoPadding = Base64Impl!('-', '_', Base64.NoPadding); ///pure @safe unittest{    ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef];    assert(Base64URLNoPadding.encode(data) == "g9cwe-8");    assert(Base64URLNoPadding.decode("g9cwe-8") == data);} /** * Template for implementing Base64 encoding and decoding. * * For most purposes, direct usage of this template is not necessary; instead, * this module provides default implementations: $(LREF Base64), implementing * basic Base64 encoding, and $(LREF Base64URL) and $(LREF Base64URLNoPadding), * that implement the Base64 variant for use in URLs and filenames, with * and without padding, respectively. * * Customized Base64 encoding schemes can be implemented by instantiating this * template with the appropriate arguments. For example: * * ----- * // Non-standard Base64 format for embedding in regular expressions. * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); * ----- * * NOTE: * Encoded strings will not have any padding if the `Padding` parameter is * set to `NoPadding`. */template Base64Impl(char Map62th, char Map63th, char Padding = '='){    enum NoPadding = '\0';  /// represents no-padding encoding      // Verify Base64 characters    static assert(Map62th < 'A' || Map62th > 'Z', "Character '" ~ Map62th ~ "' cannot be used twice");    static assert(Map63th < 'A' || Map63th > 'Z', "Character '" ~ Map63th ~ "' cannot be used twice");    static assert(Padding < 'A' || Padding > 'Z', "Character '" ~ Padding ~ "' cannot be used twice");    static assert(Map62th < 'a' || Map62th > 'z', "Character '" ~ Map62th ~ "' cannot be used twice");    static assert(Map63th < 'a' || Map63th > 'z', "Character '" ~ Map63th ~ "' cannot be used twice");    static assert(Padding < 'a' || Padding > 'z', "Character '" ~ Padding ~ "' cannot be used twice");    static assert(Map62th < '0' || Map62th > '9', "Character '" ~ Map62th ~ "' cannot be used twice");    static assert(Map63th < '0' || Map63th > '9', "Character '" ~ Map63th ~ "' cannot be used twice");    static assert(Padding < '0' || Padding > '9', "Character '" ~ Padding ~ "' cannot be used twice");    static assert(Map62th != Map63th, "Character '" ~ Map63th ~ "' cannot be used twice");    static assert(Map62th != Padding, "Character '" ~ Padding ~ "' cannot be used twice");    static assert(Map63th != Padding, "Character '" ~ Padding ~ "' cannot be used twice");    static assert(Map62th != NoPadding, "'\\0' is not a valid Base64character");    static assert(Map63th != NoPadding, "'\\0' is not a valid Base64character");      /* Encode functions */      private immutable EncodeMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ~ Map62th ~ Map63th;      /**     * Calculates the length needed to store the encoded string corresponding     * to an input of the given length.     *     * Params:     *  sourceLength = Length of the source array.     *     * Returns:     *  The length of a Base64 encoding of an array of the given length.     */    @safe @nogc    pure nothrow size_t encodeLength(in size_t sourceLength)    {        static if (Padding == NoPadding)            return (sourceLength / 3) * 4 + (sourceLength % 3 == 0 ? 0 : sourceLength % 3 == 1 ? 2 : 3);        else            return (sourceLength / 3 + (sourceLength % 3 ? 1 : 0)) * 4;    }     ///    @safe unittest    {        ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];         // Allocate a buffer large enough to hold the encoded string.        auto buf = new char[Base64.encodeLength(data.length)];         Base64.encode(data, buf);        assert(buf == "Gis8TV1u");    }      // ubyte[] to char[]      /**     * Encode $(D_PARAM source) into a `char[]` buffer using Base64     * encoding.     *     * Params:     *  source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)     *           to _encode.     *  buffer = The `char[]` buffer to store the encoded result.     *     * Returns:     *  The slice of $(D_PARAM buffer) that contains the encoded string.     */    @trusted    pure char[] encode(R1, R2)(const scope R1 source, return scope R2 buffer)    if (isArray!R1 && is(ElementType!R1 : ubyte) && is(R2 == char[]))    in    {        assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");    }    out(result)    {        assert(result.length == encodeLength(source.length), "The length of result is different from Base64");    }    do    {        immutable srcLen = source.length;        if (srcLen == 0)            return [];         immutable blocks = srcLen / 3;        immutable remain = srcLen % 3;        auto      bufptr = buffer.ptr;        auto      srcptr = source.ptr;         foreach (Unused; 0 .. blocks)        {            immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2];            *bufptr++ = EncodeMap[val >> 18       ];            *bufptr++ = EncodeMap[val >> 12 & 0x3f];            *bufptr++ = EncodeMap[val >>  6 & 0x3f];            *bufptr++ = EncodeMap[val       & 0x3f];            srcptr += 3;        }         if (remain)        {            immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0);            *bufptr++ = EncodeMap[val >> 18       ];            *bufptr++ = EncodeMap[val >> 12 & 0x3f];             final switch (remain)            {            case 2:                *bufptr++ = EncodeMap[val >> 6 & 0x3f];                static if (Padding != NoPadding)                    *bufptr++ = Padding;                break;            case 1:                static if (Padding != NoPadding)                {                    *bufptr++ = Padding;                    *bufptr++ = Padding;                }                break;            }        }         // encode method can't assume buffer length. So, slice needed.        return buffer[0 .. bufptr - buffer.ptr];    }     ///    @nogc nothrow @safe unittest    {        ubyte[6] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];        char[32] buffer;    // much bigger than necessary         // Just to be sure...        auto encodedLength = Base64.encodeLength(data.length);        assert(buffer.length >= encodedLength);         // encode() returns a slice to the provided buffer.        auto encoded = Base64.encode(data[], buffer[]);        assert(encoded is buffer[0 .. encodedLength]);        assert(encoded == "g9cwegE/");    }      // InputRange to char[]      /**     * ditto     */    char[] encode(R1, R2)(R1 source, R2 buffer)    if (!isArray!R1 && isInputRange!R1 &&        is(ElementType!R1 : ubyte) && hasLength!R1 &&        is(R2 == char[]))    in    {        assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");    }    out(result)    {        // @@@BUG@@@ D's DbC can't caputre an argument of function and store the result of precondition.        //assert(result.length == encodeLength(source.length), "The length of result is different from Base64");    }    do    {        immutable srcLen = source.length;        if (srcLen == 0)            return [];         immutable blocks = srcLen / 3;        immutable remain = srcLen % 3;        auto      bufptr = buffer.ptr;         foreach (Unused; 0 .. blocks)        {            immutable v1 = source.front; source.popFront();            immutable v2 = source.front; source.popFront();            immutable v3 = source.front; source.popFront();            immutable val = v1 << 16 | v2 << 8 | v3;            *bufptr++ = EncodeMap[val >> 18       ];            *bufptr++ = EncodeMap[val >> 12 & 0x3f];            *bufptr++ = EncodeMap[val >>  6 & 0x3f];            *bufptr++ = EncodeMap[val       & 0x3f];        }         if (remain)        {            size_t val = source.front << 16;            if (remain == 2)            {                source.popFront();                val |= source.front << 8;            }             *bufptr++ = EncodeMap[val >> 18       ];            *bufptr++ = EncodeMap[val >> 12 & 0x3f];             final switch (remain)            {            case 2:                *bufptr++ = EncodeMap[val >> 6 & 0x3f];                static if (Padding != NoPadding)                    *bufptr++ = Padding;                break;            case 1:                static if (Padding != NoPadding)                {                    *bufptr++ = Padding;                    *bufptr++ = Padding;                }                break;            }        }         // @@@BUG@@@ Workaround for DbC problem. See comment on 'out'.        version (StdUnittest)            assert(                bufptr - buffer.ptr == encodeLength(srcLen),                "The length of result is different from Base64"            );         // encode method can't assume buffer length. So, slice needed.        return buffer[0 .. bufptr - buffer.ptr];    }      // ubyte[] to OutputRange      /**     * Encodes $(D_PARAM source) into an     * $(REF_ALTTEXT output range, isOutputRange, std,range,primitives) using     * Base64 encoding.     *     * Params:     *  source = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives)     *           to _encode.     *  range  = The $(REF_ALTTEXT output range, isOutputRange, std,range,primitives)     *           to store the encoded result.     *     * Returns:     *  The number of times the output range's `put` method was invoked.     */    size_t encode(E, R)(scope const(E)[] source, auto ref R range)    if (is(E : ubyte) && isOutputRange!(R, char) && !is(R == char[]))    out(result)    {        assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");    }    do    {        immutable srcLen = source.length;        if (srcLen == 0)            return 0;         immutable blocks = srcLen / 3;        immutable remain = srcLen % 3;        auto s = source; // copy for out contract length check        size_t pcount;         foreach (Unused; 0 .. blocks)        {            immutable val = s[0] << 16 | s[1] << 8 | s[2];            put(range, EncodeMap[val >> 18       ]);            put(range, EncodeMap[val >> 12 & 0x3f]);            put(range, EncodeMap[val >>  6 & 0x3f]);            put(range, EncodeMap[val       & 0x3f]);            s = s[3 .. $];            pcount += 4;        }         if (remain)        {            immutable val = s[0] << 16 | (remain == 2 ? s[1] << 8 : 0);            put(range, EncodeMap[val >> 18       ]);            put(range, EncodeMap[val >> 12 & 0x3f]);            pcount += 2;             final switch (remain)            {            case 2:                put(range, EncodeMap[val >> 6 & 0x3f]);                pcount++;                 static if (Padding != NoPadding)                {                    put(range, Padding);                    pcount++;                }                break;            case 1:                static if (Padding != NoPadding)                {                    put(range, Padding);                    put(range, Padding);                    pcount += 2;                }                break;            }        }         return pcount;    }     ///    @safe pure nothrow unittest    {        import std.array : appender;         auto output = appender!string();        ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];         // This overload of encode() returns the number of calls to the output        // range's put method.        assert(Base64.encode(data, output) == 8);        assert(output.data == "Gis8TV1u");    }      // InputRange to OutputRange      /**     * ditto     */    size_t encode(R1, R2)(R1 source, auto ref R2 range)    if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) &&        hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char))    {        immutable srcLen = source.length;        if (srcLen == 0)            return 0;         immutable blocks = srcLen / 3;        immutable remain = srcLen % 3;        size_t    pcount;         foreach (Unused; 0 .. blocks)        {            immutable v1 = source.front; source.popFront();            immutable v2 = source.front; source.popFront();            immutable v3 = source.front; source.popFront();            immutable val = v1 << 16 | v2 << 8 | v3;            put(range, EncodeMap[val >> 18       ]);            put(range, EncodeMap[val >> 12 & 0x3f]);            put(range, EncodeMap[val >>  6 & 0x3f]);            put(range, EncodeMap[val       & 0x3f]);            pcount += 4;        }

Lines 1–500 of 2202.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t